<?php
namespace ProVM\Common\Factory;

use ProVM\Common\Implementation\Model as BaseModel;

class Model {
  protected $data_folder;
  protected $image_folder;
  public function __construct(string $data_folder, string $image_folder) {
    $this->data_folder = $data_folder;
    $this->image_folder = $image_folder;
  }

  public function getFolder(): string {
    return $this->data_folder;
  }
  public function get($file) {
    $filename = implode(DIRECTORY_SEPARATOR, [
      $this->data_folder,
      $file
    ]);
    return json_decode(trim(file_get_contents($filename)));
  }
  protected function reset() {
    $this->class = null;
    $this->conditions = null;
    $this->limits = null;
  }
  protected $class;
  public function find(string $class): Model {
    if (!class_exists($class) or !is_subclass_of($class, \ProVM\Common\Implementation\Model::class)) {
      throw new \InvalidArgumentException($class . ' is not defined.');
    }
    $this->reset();
    $this->class = $class;
    return $this;
  }
  /**
   * JSON
   * [
   *  {
   *    <field>,
   *    <value>,
   *    <operator>
   *  },
   *  ...
   * ]
   * @var array
   */
  protected $conditions;
  public function where(array $conditions): Model {
    if ($this->conditions === null) {
      $this->conditions = [];
    }
    foreach ($conditions as $condition) {
      $c = ['field' => $condition[0], 'value' => $condition[1], 'operator' => '=='];
      if (isset($condition[2])) {
        $c['operator'] = $condition[2];
      }
      $this->conditions []= (object) $c;
    }
    return $this;
  }
  protected function parseWhere(array $data): array {
    if ($this->conditions === null) {
      return $data;
    }
    foreach ($this->conditions as $condition) {
      $str = implode(' ', [
        $condition->field,
        $condition->operator,
        (is_string($condition->value)) ? "'" . $condition->value . "'" : $condition->value
      ]);
      return array_filter($data, function($item) use ($str) {
        $bool = true;
        eval("\$bool = (\$item->" . $str . ');');
        return $bool;
      });
    }
  }
  protected $limits;
  public function limit(int $limit): Model {
    if ($this->limits === null) {
      $this->limits = (object) ['limit' => null, 'offset' => 0];
    }
    $this->limits->limit = $limit;
    return $this;
  }
  public function offset(int $offset): Model {
    if ($this->limits === null) {
      $this->limits = (object) ['limit' => null, 'offset' => 0];
    }
    $this->limits->offset = $offset;
    return $this;
  }
  protected function parseLimits(array $data): array {
    if ($this->limits === null or $this->limits->limit === null) {
      return $data;
    }
    return array_slice($data, $this->limits->offset, $this->limits->limit);
  }
  protected function getFilename(): string {
    $data = explode("\\", $this->class);
    return implode(DIRECTORY_SEPARATOR, [
      $this->data_folder,
      str_replace(' ', '_', mb_strtolower(array_pop($data))) . 's.json'
    ]);
  }
  protected function build(): array {
    $filename = $this->getFilename();
    $data = json_decode(trim(file_get_contents($filename)));
    $data = $this->parseWhere($data);
    $data = $this->parseLimits($data);
    return $data;
  }
  protected function fillObject($data): BaseModel {
    $obj = new $this->class;
    $obj->map($data);
    $obj->setFactory($this);
    $obj->setImageFolder($this->image_folder);
    return $obj;
  }
  public function one(): BaseModel {
    $data = $this->limit(1)->build();
    $item = $data[0];
    $obj = $this->fillObject($item);
    return $obj;
  }
  public function many(): array {
    $data = $this->build();
    foreach ($data as &$item) {
      $obj = $this->fillObject($item);
      $item = $obj;
    }
    return $data;
  }
  public function array(): array {
    return $this->build();
  }

  public function create(string $class, $data) {
    $obj = new $class;
    $obj->map($data);
    $obj->setFactory($this);
    $obj->setImageFolder($this->image_folder);
    return $obj;
  }
}
