package App::POS::Counters;
use Mouse;
use Data::Dumper;
use POSIX qw( strftime );
use Carp;

has 'name' => (
  is => 'ro',
  isa => 'Str',
  required => 1,
);

has 'db' => (
  is => 'ro',
  isa => 'App::POS::DB::Postgres',
  required => 1,
);

sub BUILD {
  my $self = shift;
}

# returns an array or arrayref with all the keys in the dataset
sub keys {
  my $self = shift;

  my $keys = [];

  my $rec = $self->db()->select(qq{
    SELECT key
    FROM counters
    WHERE name = ?
  }, $self->name());

  if (scalar @$rec) {
    $keys = $rec;
  }

  return wantarray? @$keys : $keys;
}

# returns true if a certain key exists
sub exists {
  my $self = shift;
  my $key_name = shift;
  croak "Missing parameter..." unless defined $key_name;

  my $rec = $self->db()->select(qq{
    SELECT value
    FROM counters
    WHERE name = ? AND key = ?
  }, $self->name(), $key_name);

  if (scalar @$rec) {
    return 1;
  }
  else {
    return 0;
  }
}

sub lock {
  my $self = shift;
  my $key_name = shift;
  croak "Missing parameter..." unless defined $key_name;

  $self->db()->do("UPDATE counters SET locked = 1 WHERE name = ? AND key = ?", $self->name(), $key_name);
}

sub unlock {
  my $self = shift;
  my $key_name = shift;
  croak "Missing parameter..." unless defined $key_name;

  $self->db()->do("UPDATE counters SET locked = 0 WHERE name = ? AND key = ?", $self->name(), $key_name);
}

# return an hash or hashref with all key => value in the dataset
sub all_key_values {
  my $self = shift;

  my $r = {};

  my $rec = $self->db()->select(qq{
    SELECT key, value
    FROM counters
    WHERE name = ?
  }, $self->name());

  if (scalar @$rec) {
    for (@$rec) {
      $r->{$_->{key}} = $_->{value};
    }
  }

  return wantarray? %$r : $r;
}

# return the actual value of a certain key
sub cur_value {
  my $self = shift;
  my $key_name = shift;
  croak "Missing parameter..." unless defined $key_name;

  my $rec = $self->db()->select(qq{
    SELECT value, locked
    FROM counters
    WHERE name = ? AND key = ?
  }, $self->name(), $key_name);

  if (scalar @$rec) {
    if ($rec->[0]->{locked} == 1) {
      my $count = 0;

      while (1) {
        print STDERR $self->name()." > $key_name > is locked... waiting it unlocks...\n";

        $rec = $self->db()->select(qq{
          SELECT value, locked
          FROM counters
          WHERE name = ? AND key = ?
        }, $self->name(), $key_name);

        # prevent infinitive loop
        if ($count++ == 4) {
          my $v = $rec->[0]->{value};

          # we were 10 times on the loop, return the value and unlock the key
          $self->db()->do(
            "UPDATE counters SET locked = 0 WHERE name = ? AND key = ?",
            $self->name(), $key_name,
          );

          return $v;
        }
        elsif ($rec->[0]->{locked} == 0) {
          return $rec->[0]->{value};
        }

        sleep 1;
      }
    }
    else {
      return $rec->[0]->{value};
    }
  }
  else {
    return undef;
  }
}

# cleans all the infor for a certain key
sub clean {
  my $self = shift;

  $self->db()->do(
    "DELETE FROM counters WHERE name = ?",
    $self->name(),
  );
}

# defines a value for a key
sub set_value {
  my $self = shift;
  my $key_name = shift;
  my $value = shift;
  croak "Missing 'key_name' parameter..." unless defined $key_name;
  croak "Missing 'value' parameter..." unless defined $value;

  my $db = $self->db();
  my $dbh = $self->db()->dbh();

  $db->do(
    "DELETE FROM counters WHERE name = ? AND key = ?",
    $self->name(),
    $key_name,
  );

  $db->copy_to(qq{
    COPY counters (name, key, value, updated)
    FROM stdin
  }, $self->name(), $key_name, $value, &strftime("%Y-%m-%d %H:%M:%S", localtime));

  #$db->do(
  #  "INSERT INTO counters (name, key, value, updated) VALUES (?, ?, ?, ?)",
  #  $self->name(), $key_name, $value, &strftime("%Y-%m-%d %H:%M:%S", localtime),
  #);

}

# increment by 1 a certain key
sub increment {
  my $self = shift;
  my $key_name = shift;
  croak "Missing parameter..." unless defined $key_name;

  my $ret = 1;

  my $rec = $self->db()->select(qq{
    SELECT value
    FROM counters
    WHERE name = ? AND key = ?
  }, $self->name(), $key_name);

  if (! scalar @$rec) {
    $self->db()->do(qq{
      INSERT INTO counters VALUES (?, ?, ?)
    }, $self->name(), $key_name, 1);
  }
  else {
    $ret = $rec->[0]->{value} + 1;

    $self->db()->do(qq{
      UPDATE counters SET value = ?, updated = ? WHERE name = ? AND key = ?
    }, $ret, &strftime("%Y-%m-%d %H:%M:%S", localtime), $self->name(), $key_name);
  }

  return $ret;
}

# adds up a value to a key
sub addup {
  my $self = shift;
  my $key_name = shift;
  my $amount = shift;
  croak "Missing 'key_name' parameter..." unless defined $key_name;
  croak "Missing 'amount' parameter..." unless defined $amount;

  my $ret = 1;

  my $rec = $self->db()->select(qq{
    SELECT value
    FROM counters
    WHERE name = ? AND key = ?
  }, $self->name(), $key_name);

  if (! scalar @$rec) {
    $self->db()->do(qq{
      INSERT INTO counters VALUES (?, ?, ?)
    }, $self->name(), $key_name, $amount);
  }
  else {
    $ret = $rec->[0]->{value} + $amount;

    $self->db()->do(qq{
      UPDATE counters SET value = ?, updated = ? WHERE name = ? AND key = ?
    }, $ret, &strftime("%Y-%m-%d %H:%M:%S", localtime), $self->name(), $key_name);
  }

  return $ret;
}

__PACKAGE__->meta->make_immutable();

