<?php
/**
 * @copyright NetMonsters <team@netmonsters.ru>
 * @link http://netmonsters.ru
 * @package Majestic
 * @subpackage db
 * @since 2011-11-15
 */

class MongoCommandBuilder
{

    const FIND = 'Find';

    const INSERT = 'Insert';

    const UPDATE = 'Update';

    const REMOVE = 'Remove';

    const COMMAND = 'Command';

    static public function factory($type, $collection = null)
    {
        $class = ucfirst($type) . 'MongoCommand';
        $command = new $class();
        $command->setCollection($collection);
        return $command;
    }
}

abstract class MongoDbCommand
{
    protected $collection;

    public function execute()
    {
        if ($this->checkParams()) {
            return $this->concreteExecute();
        } else {
            throw new Exception(get_called_class() . ' error. Bind all required params first.');
        }
    }

    public function bindParam($name, $value)
    {
        if (property_exists($this, $name)) {
            $this->$name = $value;
        }
        return $this;
    }

    public function setCollection($collection)
    {
        $this->collection = $collection;
        return $this;
    }

    abstract protected function concreteExecute();

    abstract protected function checkParams();
}

class FindMongoCommand extends MongoDbCommand
{
    protected $condition;

    protected $fields;

    protected $multiple = true;

    protected function concreteExecute()
    {
        if($this->multiple) {
            return $this->collection->find($this->condition, $this->fields);
        } else {
            return $this->collection->findOne($this->condition, $this->fields);
        }
    }

    protected function checkParams()
    {
        if (isset($this->collection) && isset($this->condition) && isset($this->fields)) {
            return true;
        } else {
            return false;
        }
    }
}

class InsertMongoCommand extends MongoDbCommand
{
    protected $data;

    protected $safe = true;

    protected $insertId = false;

    protected function concreteExecute()
    {
        $result = $this->collection->insert($this->data, array('safe' => $this->safe));
        $this->insertId = $this->data['_id'];
        return $result;
    }

    protected function checkParams()
    {
        if (isset($this->collection) && isset($this->data) && isset($this->safe)) {
            return true;
        } else {
            return false;
        }
    }

    public function getInsertId()
    {
        return $this->insertId;
    }
}

class UpdateMongoCommand extends MongoDbCommand
{
    protected $condition;

    protected $data;

    protected $multiple = true;

    protected $upsert = false;

    protected $safe = true;

    protected function concreteExecute()
    {
        return $this->collection->update($this->condition, $this->data,
                                         array('multiple' => $this->multiple, 'upsert' => $this->upsert, 'safe' => $this->safe));
    }

    protected function checkParams()
    {
        if (isset($this->collection) && isset($this->condition) && isset($this->data) &&
            isset($this->upsert) && isset($this->safe)
        ) {
            return true;
        } else {
            return false;
        }
    }
}

class RemoveMongoCommand extends MongoDbCommand
{
    protected $condition;

    protected $safe = true;

    protected function concreteExecute()
    {
        return $this->collection->remove($this->condition, array('safe' => $this->safe));
    }

    protected function checkParams()
    {
        if (isset($this->collection) && isset($this->condition) && isset($this->safe)) {
            return true;
        } else {
            return false;
        }
    }
}

 class CommandMongoCommand extends MongoDbCommand
 {
     protected $command;

     protected function concreteExecute()
     {
         $db = $this->collection->db;
         if($db instanceof MongoDB) {
             return $db->command($this->command);
         } else {
             return false;
         }
     }

    protected function checkParams()
    {
        if (isset($this->collection) && isset($this->command)) {
            return true;
        } else {
            return false;
        }
    }
 }