###################################################################

package KoLBot::Items;
 
# Copyright (c) 2004 Alan W. Mahoney. All rights reserved. 
# This program is free software.  You may modify and/or 
# distribute it under the same terms as Perl itself.  
# This copyright notice must remain attached to the file.  
#

use warnings;
use strict;

use KoLBot::Item;

use Carp;

use POSIX qw( floor );

BEGIN 
{
  use Exporter ();
  our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $ID);

  $VERSION     = 0.5;
  $ID          = '$Id: Items.pm 176 2006-11-22 14:01:17Z alan $';

  @ISA         = qw(Exporter);
  @EXPORT      = qw();
  %EXPORT_TAGS = ( );

  @EXPORT_OK   = qw();
}

our @EXPORT_OK;

##########################################
## element access
##########################################

for my $field (qw( 
		   by_name
		   by_id
		   by_desc
		   )) {
  no strict "refs";
  *$field = sub {
    my $self = shift;
    $self->{$field} = shift if @_;
    return $self->{$field};
  }
}

##########################################
## Constructor:
##########################################

# Needs to be called with a full config hashref.  See examples.

sub new {
  my $invocant = shift;
  my $class = ref($invocant) || $invocant;
  my $items = { @_ };

  $items->{config}->{no_check} ||= 0;
  $items->{config}->{update} ||= 0;
  $items->{config}->{update_db} ||= 0;

  $items->{by_name} = {};
  $items->{by_id} = {};
  $items->{by_desc} = {};

  bless $items, $class;
}

##########################################
## The rest
##########################################

sub add {
  my $items = shift;
  my $item = shift;

  $items->{by_name}->{lc($item->name)} = $item;
  $items->{by_name}->{lc($item->plural)} = $item;
  $items->{by_id}->{$item->id} = $item;
  $items->{by_desc}->{$item->desc} = $item;
}

sub init {
  my $items = shift;

  if($items->{config}->{dbh}) {
    my $sql = qq{
      SELECT  *
	FROM kol_items 
    };
    my $dbi = $items->{config}->{dbh}->prepare($sql);
    $dbi->execute();

    while (my $row = $dbi->fetchrow_hashref) {
      my $i = KoLBot::Item->new(%$row);
      $i->{desc} = $i->{desc_id};
      delete($i->{desc_id});
      $i->{id} = $i->{item_id};
      delete($i->{item_id});
      
      $items->add($i);
    }
    $dbi->finish();
    return;
  }

  if($items->{config}->{file}) {
    open ITEMS,"<",$items->{config}->{file}
    or die "Can't open the file " . $items->{config}->{file};

    while (<ITEMS>) {
      chomp;
      /^\s*$/ and next;
      /^#/ and next;
      /^\s*(\d+)\s+(\d+)\s+(.*?)(\s*:\s*(.*?))?\s*$/ or die "Can't read line $_";
      $items->add(KoLBot::Item->new(desc   => $2,
				    id     => $1,
				    name   => $3,
				    plural => $5,
				    ));
    }
    close ITEMS;
    return;
  }
  die "no way to get items data";
}

# can take 
#  desc # description number
#  name # name
#  id   # id number

sub get_item {
  my $items = shift;
  my %params = @_;

  my $ri;

  ($params{id})
    and $ri = $items->by_id->{$params{id}};
  (!$ri && $params{desc})
    and $ri = $items->by_desc->{$params{desc}};
  (!$ri && exists($params{name}))
    and $ri = $items->by_name->{lc($params{name})};

  unless (defined($ri)) {
    $ri = $items->update($ri,%params);
  }
  unless (defined($ri)) {
    return $ri;
  }

  if(exists($params{name})
     && lc($params{name}) ne lc($ri->name)
     && lc($params{name}) ne lc($ri->plural)
     ||
     exists($params{id}) && $params{id} != $ri->id
     || 
     exists($params{desc}) && $params{desc} != $ri->desc
     ) {
    print "Item lookup mismatch.  Passed data: \n"
      . "  Number: " . ($params{number} || '')
      . " Id: " . ($params{id} || '') 
      . " Desc: " . ($params{desc} || '')
      . " Name: " . ($params{name} || '') . "\n"
      . "  Returned data:\n"
      . "  Id: " . ($ri->{id} || '') 
      . " Desc: " . ($ri->{desc} || '')
      . " Name: " . ($ri->{name} || '')
      . " Plural: " . ($ri->{plural} || '')
      . "\n";

    $ri = $items->update($ri,%params);
  }

  ## AWM: if failed
  # $params{anal} and return undef;

  # make a copy of the data to return, stick the number in
  $ri = new KoLBot::Item(%$ri);
  $ri->{number} = $params{number} || 0;
  return $ri;
}

sub update {
  my $items = shift;
  my $ri = shift;
  my %params = (number => 1,
		@_);

  return $ri if $params{no_check};

  return $ri
    unless $items->{config}->{check}
    || $items->{config}->{update}
    || $items->{config}->{update_db};

  unless (exists($items->{config}->{bot}) 
	  && $items->{config}->{bot}->isa("KoLBot")
	  ) {
    print $items->{config}->{bot},"\n";
    return $ri;
  }

  my $bot = $items->{config}->{bot};

  if($params{number} > 1 
     && !exists($params{desc}) 
     && !exists($params{id})) {
    print "Items::update can't handle a named plural.\n";
    return $ri;
  }

  # attempt to get info from combine page: singulars and item ids.
  my $c_inv = $bot->combine_for_inventory();
  if(!defined($c_inv)) {
    my ($rv,$txt,$results) =
      $bot->inventory_makestuff(whichitem => 25,
				quantity => 1);
    if($rv) {
      print "Items::update: problem making meat paste ($txt), not updating\n";
      return $ri;
    }
    
    $c_inv = $bot->combine_for_inventory();
  }
  unless (defined($c_inv)) {
    print "Items::update: no meat paste twice, not updating\n",
    return $ri;
  }

  # get info from inventory pages: singulars and desc ids, sometimes
  # item ids
  my $inv1 = $bot->get_inventory(1,0,1);
  my $inv2 = $bot->get_inventory(2,0,1);
  my $inv3 = $bot->get_inventory(3,0,1);

  # create name lookup on combine page
  my %items_new;
  for (values %$c_inv) {
    $items_new{lc($_->{name})} = new KoLBot::Item(%$_);
  }

  # add description IDs from inventory
  for((@$inv1,@$inv2,@$inv3)) {
    unless(exists($items_new{lc($_->{name})})) {
#      print "Items::update: no combine entry for inventory entry "
#        . $_->{name} . "\n";
      next;
    }
    my $i = $items_new{lc($_->{name})};
    if($i->{name} ne $_->{name}) {
      print "Items::update: name mismatch, combine = " . $i->{name}
        . ", inventory = " . $_->{name} . "\n";
      delete $items_new{lc($_->{name})};
      next;
    }
    if(exists($_->{id}) && $i->{id} != $_->{id}) {
      print "Items::update: id mismatch, name = " . $_->{name}
        . ", combine id = " . $i->{id}
        . ", inventory id = " . $_->{id} . "\n";
      delete $items_new{lc($_->{name})};
      next;
    }
    
    $items_new{lc($_->{name})}->{desc} = $_->{desc};
  }

  # check to be sure that we've found description IDs for all items
  for(values %items_new) {
    next if exists($_->{desc});
    print "Items::update: no description found in inventory for item "
      . $_->{name} . " (" . $_->{id} . "), deleting\n";
    delete($items_new{lc($_->{name})});
  }

  # look for our item
  my $new_ri;
  for(values %items_new) {
    if(exists($params{id}) && $params{id} == $_->{id}) {
      $new_ri = $_;
      last;
    } 
    if (exists($params{desc}) && $params{desc} == $_->{desc}) {
      $new_ri = $_;
      last;
    }
    if (exists($params{name}) && lc($params{name}) eq lc($_->{name})) {
      $new_ri = $_;
      last;
    }
  }

  if(!defined($new_ri)) {
    print "Items::update couldn't match item passed.  Not updating.\n";
    print "params{name} = ",$params{name},"\n";
    return $ri;
  }

  if(exists($params{id}) && $params{id} != $new_ri->{id}) {
    print "Items::update mismatched id.  Not updating.\n";
    return $ri;
  }
  if (exists($params{desc}) && $params{desc} != $new_ri->{desc}) {
    print "Items::update mismatched desc.  Not updating.\n";
    return $ri;
  }

  # if sent with a name different than what is in the inventory,
  # it is likely that it is a plural
  # note that params{number} > 1 might have a singular
  if(exists($params{name})
     &&
     $params{number} > 1
     &&
     $params{name} ne $new_ri->{name}) {
    $new_ri->{plural} = $params{name};
  }

  # We have a complete, consistent item (possibly with plural)

  return $new_ri
    unless $items->{config}->{update} || $items->{config}->{update_db};

  # find a potential item to delete
  my $del_ri = $items->by_id->{$new_ri->{id}};

  my $ri2 = $items->by_desc->{$new_ri->{desc}};
  if(defined($ri2)) {
    if(defined($del_ri) && $ri2->as_string() ne $del_ri->as_string()) {
      print "Items::update, more than one item to delete, not doing it.\n";
      return $new_ri;
    }
    $del_ri = $ri2;
  }

  if(defined($del_ri) && exists($del_ri->{name})) {
    my $ri3 = $items->by_name->{lc($del_ri->{name})};

    if(defined($ri3)) {
      if(defined($del_ri) && $ri3->as_string() ne $del_ri->as_string()) {
	print "Items::update, more than one item to delete, not doing it.\n";
	return $new_ri;
      }
      $del_ri = $ri3;
    }
  }

  if(defined($del_ri)) {
    print "Items::update deleting item " . $del_ri . "\n";
    # merge plurals
    if($del_ri->{id} == $new_ri->{id} 
       &&
       !exists($new_ri->{plural})
       &&
       exists($del_ri->{plural})
       &&
       $del_ri->{plural} ne ""
       ) {
      $new_ri->{plural} = $del_ri->{plural};
    }

    # update local
    delete $items->{by_name}->{lc($del_ri->{name})};
    delete $items->{by_name}->{lc($del_ri->{plural})};
    delete $items->{by_id}->{$del_ri->{id}};
    delete $items->{by_desc}->{$del_ri->{desc}};
  }

  print "Items::update adding item " . $new_ri . "\n";

  $items->add($new_ri);

  return $new_ri
    unless $items->{config}->{update_db};

  unless (exists($items->{config}->{dbh})) {
    print "Need a database to run a database update.\n";
    return $ri;
  }

  my $dbh = $items->{config}->{dbh};

  # update database
  my $sql = qq{
    DELETE FROM kol_items WHERE item_id = ?
    };
  my $dbi = $items->{config}->{dbh}->prepare($sql);
  $dbi->execute($del_ri->{id});
  $dbi->finish;

  $sql = qq{
    INSERT INTO kol_items (item_id,desc_id,name,plural)
      VALUES (?,?,?,?)
    };
  $dbi = $items->{config}->{dbh}->prepare($sql);
  $dbi->execute($new_ri->{id},$new_ri->{desc},$new_ri->{name},
		$new_ri->{plural} || '');
  $dbi->finish;

  return $new_ri;
}

END {} ;

1;

