<?php

class Pampa_Db_Table extends Zend_Db_Table_Abstract
{
  /**
   * The row data
   * @var Array
   */
  protected $_data;

  /**
   * The table order field
   * @var String
   */
  protected $_orderField = 'order_id';

  /**
   * if the table uses the update fields
   * @var Boolean
   */
  protected $_useUpdateFields = true;

  /**
   * Insert and retrieves the inserted row
   * @param Array $data
   * @return Zend_Db_Table_Row
   */
  public function insertRow($data)
  {
    try
    {
      // change the object to array
      if (is_object($data))
      {
        $data = Pampa_Util_Array::fromObject($data);
      }
      $this->_data = $data;

      // check for hook functions
      if (method_exists($this, 'before'))
      {
        $this->before();
      }
      if (method_exists($this, 'beforeInsert'))
      {
        $this->beforeInsert();
      }

      // update the date field
      if ($this->_useUpdateFields === true)
      {
        $today = new Zend_Date();
        $this->_data['date_ins'] = $today->get(Zend_Date::W3C);
        $this->_data['date_upd'] = $today->get(Zend_Date::W3C);
      }

      // unset the id field
      unset($this->_data['id']);

      // insert the row and get the primary key
      $id = parent::insert($this->_data);

      // find the inserted row
      return $this->find($id)->current();

    }
    catch (exception $e)
    {

      // throws the error
      throw new Exception($e->getMessage());
    }
  }
  public function insertRowWithId($data)
  {
    try
    {
      // change the object to array
      if (is_object($data))
      {
        $data = Pampa_Util_Array::fromObject($data);
      }
      $this->_data = $data;

      // check for hook functions
      if (method_exists($this, 'before'))
      {
        $this->before();
      }
      if (method_exists($this, 'beforeInsert'))
      {
        $this->beforeInsert();
      }

      // update the date field
      if ($this->_useUpdateFields === true)
      {
        $today = new Zend_Date();
        $this->_data['date_ins'] = $today->get(Zend_Date::W3C);
        $this->_data['date_upd'] = $today->get(Zend_Date::W3C);
      }

      // unset the id field
      // unset($this->_data['id']);

      // insert the row and get the primary key
      $id = parent::insert($this->_data);

      // find the inserted row
      return $this->find($id)->current();

    }
    catch (exception $e)
    {

      // throws the error
      throw new Exception($e->getMessage());
    }
  }

  /**
   * Update and retrieves the update row
   * @param Array $data
   * @return Zend_Db_Table_Row
   */
  public function updateRow($data)
  {
    try
    {
      if (!is_array($data))
      {
        $data = Pampa_Util_Array::fromObject($data);
      }
      $this->_data = $data;

      // check for hook functions
      if (method_exists($this, 'before'))
      {
        $this->before();
      }
      if (method_exists($this, 'beforeUpdate'))
      {
        $this->beforeUpdate();
      }

      // updates date fields
      if ($this->_useUpdateFields)
      {
        $hoje = new Zend_Date();
        $this->_data['date_upd'] = $hoje->get(Zend_Date::W3C);
      }

      // retrieves and removes the id
      $id = $this->_data['id'];
      unset($this->_data['id']);

      // update
      $update = parent::update($this->_data, "id=" . $id);

      // find the updated row
      return $this->find($id)->current();

    }
    catch (exception $e)
    {
      throw new Exception($e->getMessage());
    }
  }

  /**
   * delete rows from table
   * @param String|Array $where
   * @return Integer
   */
  public function delete($where)
  {
    $return = new stdClass();
    $return->error = false;
    $return->message = '';

    try
    {
      if (method_exists($this, 'beforeDelete'))
      {
        // recupera o registro
        $row = $this->fetchRow($where);
        $this->_data = $row->toArray();

        $this->beforeDelete();
      }

      // remove o registro
      parent::delete($where);

      // procura o primeiro registro
      $linha = $this->fetchRow($this->select()->limit(1));

      if(!is_null($linha))
      {
        // seta o campo de update
        $hoje = new Zend_Date();
        $_data['date_upd'] = $hoje->get(Zend_Date::W3C);
        // atualiza
        $update = parent::update($_data, 'id = ' . $linha->id);
      }

    }
    catch (exception $e)
    {
      throw new Exception($e->getMessage());

      $return->error = true;
      $return->message = $e->getMessage();
    }
    return $return;
  }

  /**
   * delete row(s) from table
   * @param String|Array $id
   * @return Intenger
   */
  public function remove($id = false, $_where = null)
  {
    $return = new stdClass();
    $return->error = false;
    $return->message = '';

    try
    {
      if (!$id)
      {
        throw new Exception("Invalid ID");
      }

      // check if is only one row
      if (is_array($id))
      {
        foreach ($id as $reg)
        {
          if (is_numeric($reg))
          {
            $where = $this->getAdapter()->quoteInto("id = ?", (int)$reg);
            $this->delete($where);
          }
        }
      }
      else
      {
        if (is_numeric($id))
        {
          $where = $this->getAdapter()->quoteInto("id = ?", (int)$id);
          $this->delete($where);
        }
      }

    }
    catch (exception $e)
    {
      $return->error = true;
      $return->message = $e->getMessage();
    }

    // check if has order field and reorded the table
    $fields = $this->_getCols();
    if (in_array($this->_orderField, $fields))
    {
      $this->reOrder($_where);
    }

    return $return;
  }

  /**
   * return table rows by id desc
   * @param String|Array|Null $where
   * @return Zend_Db_Table_Rowset
   */
  public function fetchAllDesc($where = null)
  {
    $key = $this->_primary == null ? "id" : $this->_primary;

    $select = $this->select()->order("{$key} DESC");

    if (!empty($where))
    {
      if (is_array($where))
      {
        foreach ($where as $w)
        {
          $select->where($w);
        }
      }
      else
      {
        $select->where($where);
      }
    }

    return $this->fetchAll($select);
  }

  /**
   * return table rows by order field
   * @param String|Array|Null $where
   * @return Zend_Db_Table_Rowset
   */
  public function fetchAllOrder($where = null)
  {
    $select = $this->select()->order($this->_orderField, "ASC");

    if (!empty($where))
    {
      if (is_array($where))
      {
        foreach ($where as $w)
        {
          $select->where($w);
        }
      }
      else
      {
        $select->where($where);
      }
    }

    return $this->fetchAll($select);
  }

  /**
   * retorna a array bidimensional para uso em select
   * @param label campo para o texto, posicao 1 da array
   * @param value campo para o valor, posicao 0 da array (padrao 'id')
   * @return array a array com os valores
   */
  public function fetchForSelect($label = "", $value = "id", $where = null, $useEmpty = false, $order = null)
  {
    // define os campos a serem buscados
    $select = $this->select()->from($this->_name, array($label, $value));

    // valida se veio busca
    if ($where != null)
    {
      if (is_array($where))
      {
        foreach ($where as $w)
        {
          $select->where($w);
        }
      }
      else
      {
        $select->where($where);
      }
    }
    // valida se veio ordem
    if ($order != null)
    {
      if (is_array($order))
      {
        foreach ($order as $w)
        {
          $select->order($w);
        }
      }
      else
      {
        $select->order($order);
      }
    }
    // pega a lista de elementos
    $items = $this->fetchAll($select);

    // array de retorno
    $return = array();

    if ($useEmpty != false)
    {
      if (is_array($useEmpty))
      {
        foreach ($useEmpty as $k => $v)
        {
          $return[$k] = $v;
        }
      }
      else
      {
        // insere o primeiro registro de selecione
        $return[""] = $useEmpty;
      }
    }

    foreach ($items as $item)
    {
      $return[$item->$value] = $item->$label;
    }

    return $return;
  }

  /**
   * retorna a ordem registro com a maior valor + 1
   * @return Integer valor da ordem
   */
  protected function getOrder($condicao = null)
  {
    // select query
    $select = $this->select()->from($this->_name, array($this->_orderField))->order("{$this->_orderField} DESC")->limit(1);

    $sql = $select->__toString();

    // adiciona condição
    if (!empty($condicao))
    {
      if (is_array($condicao))
      {
        foreach ($condicao as $c)
        {
          $select->where($c);
        }
      }
      else
      {
        $select->where($condicao);
      }
    }

    // busca o item
    $itemOrder = $this->fetchRow($select);

    if (is_null($itemOrder))
    {
      return 1;
    }
    $itemOrder = $itemOrder->toArray();

    // valida se e nulo
    $orderField = $this->_orderField;
    $numOrder = !is_null($itemOrder[$this->_orderField]) ? intval($itemOrder[$this->_orderField]) : 0;

    // retorna o valor
    return ($numOrder + 1);
  }

  public function newOrder($ids)
  {
    //
    if(!is_array($ids))
    {
      return false;
    }
    else
    {
      $vez = 0;
      foreach ($ids as $id)
      {
        // the base row
        $baseRow = $this->find($id)->current();

        // the order field
        $orderField = $this->_orderField;

        // update the row
        $this->updateRow(array($orderField => $vez, "id" => $baseRow->id));
        $vez++;
      }
      return true;
    }
  }

  /**
   * realoca o registro de acordo com a ordem
   * usa o campo padrao ID e o campo ORDEM
   * @param Integer ID do registro
   * @param Integer Ordem do registro (1 para subir, 0 para descer);
   * @param String filtro da query
   * @return Boolean
   */
  public function setOrder($id = false, $dir = false, $where = null)
  {
    // valida
    if (!$id || $dir === false)
      return false;

    try
    {

      // the base row
      $baseRow = $this->find($id)->current();

      // the order field
      $orderField = $this->_orderField;

      // query for the changed row
      $select = $this->select()->where($dir == '1' ? "$orderField < ?" : "$orderField > ?", $baseRow->$orderField)->order($dir == "1" ? "$orderField DESC" : "$orderField ASC")->limit(1);

      // if there's a filter
      if ($where != null)
      {
        $select->where($where);
      }

      $query = $select->__toString();

      // get the changed row
      $changeRow = $this->fetchRow($select);

      if (is_null($changeRow))
      {
        return true;
      }

      // change the orders
      $this->updateRow(array($orderField => $changeRow->$orderField, "id" => $baseRow->id));
      $this->updateRow(array($orderField => $baseRow->$orderField, "id" => $changeRow->id));

      return true;
    }
    catch (exception $e)
    {
      Pampa_Util_Dump::text($e->getMessage());
      return false;
    }

  }

  /**
   *
   * @param <Integer> $id
   * @return <Zend_Db_Row_Abstract>
   */
  public function isValidId($id, $return = true)
  {
    $select = $this->select()->where("id = ?", $id);

    if (!$return)
    {
      $select->from($this->_name, array("id"));
    }

    $row = $this->fetchRow($select);

    if ($return)
    {
      return is_null($row) ? false : $row;
    }
    else
    {
      return !is_null($row);
    }

  }

  public function count($where = null)
  {
    $select = $this->select()->from($this->_name, array("COUNT(*) AS total"));

    if (!empty($where))
    {
      if (is_array($where))
      {
        foreach ($where as $w)
        {
          $select->where($w);
        }
      }
      else
      {
        $select->where($where);
      }

    }

    $result = $this->fetchRow($select);

    return $result->total;
  }

  public function reOrder($_where = null)
  {
    $orderField = $this->_orderField;

    // recupera o registro de menor ordem
    $select = $this->select()->order("{$orderField} ASC")->limit(1);
    if(!is_null($_where))
    {
      $select->where($_where);
    }

    // registro mais baixo
    $lower = $this->fetchRow($select);

    // recupera a ordem
    if(!is_null($lower))
    {
      $listOrder = $lower->$orderField;

      // busca o resto dos registros
      $select = $this->select()->order("{$orderField} ASC");
      if(!is_null($_where))
      {
        $select->where($_where);
      }
      $rows = $this->fetchAll($select);

      foreach ($rows as $r)
      {
        $this->updateRow(array($orderField => $listOrder, "id" => $r->id));
        $listOrder++;
      }
    }
  }

  /**
   * return if table use update fields
   * @return Boolean
   */
  public function useUpdateFields()
  {
    return $this->_useUpdateFields;
  }

  public function validate($data, $excludeFields = array())
  {
    if (!$this->_validateFields)
    {
      throw new Exception("Empty validation configuration");
    }

    $return = new stdClass;
    $return->error = false;
    $return->message = '';

    $currentField;
    $currentValidation;


    try
    {

      foreach ($this->_validateFields as $field => $validation)
      {
        $valid = false;
        $currentField = $data[$field];

        // check if single or multiple validation
        if (is_array($validation))
        {
          foreach ($validation as $v => $params)
          {
            // check if validation has params
            if (is_numeric($v))
            {
              $currentValidation = $params;
              $valid = Zend_Validate::is($data[$field], $params);
            }
            else
            {
              $currentField = $v;
              $valid = Zend_Validate::is($data[$field], $v, $params);
            }
          }
        }
        else
        {
          $currentValidation = $validation;
          $valid = Zend_Validate::is($data[$field], $validation);
        }


        if (!$valid)
        {
          throw new Exception("Invalid data");
          break;
        }
      }
    }
    catch (exception $e)
    {
      $return->field = $currentField;
      $return->validation = $currentValidation;
      $return->error = true;
      $return->message = $e->getMessage();
    }

    return $return;
  }

  public function setOrderField($orderField = '')
  {
    if (empty($orderField))
    {
      return;
    }
    $this->_orderField = $orderField;
  }
}
