<?php
abstract class Super {

  /**
   * Pole atributů, které lze získat metodou __get()
   * @var array
   */
  protected $getter = array();
//	protected $super_getter = array('label', 'validate', 'stavy', 'my_stav', 'state_validate', 'hash', 'table');
  /**
   * Pole atributů, které lze nastavit metodou __set()
   * @var array
   */
  protected $setter = array();

  /**
   * Pole s popisky 
   * @var array
   */
  public $label = array();
  
	/**
   * Pole s nastavením filtrace 
   * @var array
   */
  public $filtry = array();
  
  /**
   * Pole určující, typ atributů pro validaci
   * @var array
   */
  public $validate = array();
  
/**
 * Pole s možnými stavy entity
 */ 
  public $stavy = array();
  
/**
 * Aktuální stav - textová hodnota
 */ 
  public $my_stav;

  /**
   * Pole validace pro konkrétní stav
   * @var array
   */
  public $state_validate = array();

  /**
   * Proměnná pro uchování hash hodnoty záznamu
   * @var array
   */
  public $hash;

  /**
   * Název tabulky se kterou třída pracuje
   * @var string
   */
  public $table = '';

  /**
   * Komplatní validační pole
   * @var string
   */
  public $valAll = array();

  const INT_MIN = -2147483648;
  const INT_MAX = 2147483648;
  const UNINT_MIN = 0;
  const UNINT_MAX = 4294967295;

/**
 * Getter
 * @param $key
 * @return unknown_type
 */
  public function __get($key) {

    if (array_search($key, $this->getter) !== false) {
      return $this->$key;
    }
    else {
      die ("Hodnotu $key nelze číst!");
    }
  }

/**
 * Setter
 * @param $key
 * @return unknown_type
 */
  public function __set($key, $value) {
    if (array_search($key, $this->setter) !== false) {
      return $this->$key = $value;
    }
    else {
      die ("Hodnotu $key nelze měnit!");
    }
  }

/**
 * Výpočet hashe pro atributy přístupné přes getter
 * @param $key
 * @return unknown_type
 */
  public function getHash() {
    $str = 'Hash';
    foreach($this->getter as $item) {
      $str .= $this->$item;
    }
    return sha1($str);
  }

/**
 * Vrací parametry validace pro atribut
 * @param $key
 * @return array
 */
  public function validate($args) {
    if (func_num_args() < 1) {
      die('Super->validate -- předělat na vyjímku');
    }
    $args_array = func_get_args();
    $ret = true;
    foreach ($args_array as $nazev) {
      $ret = ( $ret AND $this->is_valid($this->$nazev, $this->validate[$nazev]) );
    }
    return $ret;
  }

/**
 * Validace hodnoty
 * @param $key
 * @return unknown_type
 */
  public function validate_value($hodnota, $typ) {
    return $this->is_valid($hodnota, $typ);
  }

/**
 * Validace hodnoty
 * @param $key
 * @return boolean
 */
  private function is_valid($value, $type) {
    switch($type) {
      case 'int':
        $val = intval($value);
        if ( ($val > self::INT_MIN) AND ($val < self::INT_MAX ) ) {
          if ($val == $value)
          return true;
        }
        return false;
        break;

      case 'unint':
        $val = intval($value);
        if ( ($val > self::UNINT_MIN) AND ($val < self::UNINT_MAX ) ) {
          if ($val == $value)
          return true;
        }
        return false;
        break;
    }
  }

/**
 * Načte položky přístupné přes setter z $_POST
 * @param $key
 * @return unknown_type
 */
  public function setFromPost($empty = true) {

    foreach($this->setter as $key) {	
    	
    	if ($empty) {
    		if (isset($_POST[$key])) {
    			if (!empty($_POST[$key]) OR is_numeric($_POST[$key])) {
    				$this->$key = $_POST[$key];  
    			}
    			else {
    				$this->$key = null;
    			}
    		}
    	}
    	elseif (isset($_POST[$key]) AND (!empty($_POST[$key]) OR is_numeric($_POST[$key]))) {
        $this->$key = $_POST[$key];       
      }
    }
  }

/**
 * Načte položky přístupné přes getter z databáze (při načítání 1 záznamu)
 * @param $key
 */
  protected function setFromRow($row) {
    foreach($this->getter as $key) {
      $this->$key = @$row[strtoupper($key)];
    }
  }

/**
 * Ověření změny záznamu v DB
 * @param $key
 * @return unknown_type
 */
  public function checkDiff($id, $form_id, $state_nocheck = false) {

    $old_values = $this->loadByID($id, true);
    $registry = Registry::getInstance();

    if ($this->hash != $_POST[HASH]) {

      $form = $registry->$form_id;
      $legend = $form['legend'];
      $change = array(); 
      foreach($old_values as $key=>$value) {    

        if ( $key != HASH AND $value != $_POST[$key]) {
          $change[] = "$legend[$key] : $value";
        }

      }
      return $change;

    }
    if (!$state_nocheck AND $this->checkStates($this->stav, $_POST['stav']) == false ) {
      throw new AppliException(5, ' Nemáte oprávnění pro operaci, kterou se pokoušíte provést.<br />
          	Zaznaménán pokus o narušení integrity systému.');
    }
    
    return false;
    
  }

/**
 * Ověření přístupu k záznamu podle stavu
 * @param $key
 * @return unknown_type
 */
  public function checkStates($start, $cil) {
    $registry = Registry::getInstance();
    if ($start == $cil) {
      $result = dibi::query('SELECT 1 AS BOOL FROM UZIVATEL U 
		INNER JOIN UZIVATEL_ROLE UR ON UR.ID_UZIV = U.ID 
		INNER JOIN STAVY_PRECHOD SP ON SP.ID_ROLE = UR.ID_ROLE AND SP.ENTITA = %s AND SP.ID_STAV_START = %i
		WHERE U.ID = %i', $this->table, $start, $registry->user->id);
      
      return $res = $result->fetchSingle('BOOL');
    }
    else {
      $result = dibi::query('SELECT 1 AS BOOL FROM UZIVATEL U 
		INNER JOIN UZIVATEL_ROLE UR ON UR.ID_UZIV = U.ID 
		INNER JOIN STAVY_PRECHOD SP ON SP.ID_ROLE = UR.ID_ROLE AND
			SP.ENTITA = %s AND SP.ID_STAV_START = %i AND SP.ID_STAV_CIL = %i
		WHERE U.ID = %i', $this->table, $start, $cil, $registry->user->id);
      
      return $res = $result->fetchSingle('BOOL');
    }
  }

/**
 * INSERT do DB
 * @param $key
 */
  public function insert() {
  	if (method_exists($this, 'beforeInsert')) {
  		$this->beforeInsert();
  	}
  	$res = dibi::query('INSERT INTO ['.$this->table.']', $this->toArray(true) );
  	$this->id = dibi::insertId();
  }

/**
 * UPDATE v DB
 * @param $key
 */
  public function update() {
    if (method_exists($this, 'beforeUpdate')) {
  		$this->beforeUpdate();
  	}
    $res = dibi::query('UPDATE ['.$this->table.'] SET', $this->toArrayNN(true),' WHERE ID = %i',  $this->id );
  }

/**
 * Načtení možných stavů do $this->stavy
 * @param $key
 */
  public function myStates() {
    $registry = Registry::getInstance();
    $result = dibi::query('SELECT S.STAV_ID, S.NAZEV, S.POPIS FROM UZIVATEL U
			INNER JOIN UZIVATEL_ROLE UR ON UR.ID_UZIV = U.ID 
			INNER JOIN STAVY_PRECHOD SP ON SP.ID_ROLE = UR.ID_ROLE AND SP.ID_STAV_START = %i AND SP.ENTITA = %s 
			INNER JOIN STAV S ON (S.STAV_ID = SP.ID_STAV_CIL OR S.STAV_ID = SP.ID_STAV_START) AND S.ENTITA = SP.ENTITA
			WHERE U.ID = %i',
    $this->stav , $this->table, $registry->user->id );

    $r = $result->fetchAssoc('STAV_ID');
    $stavy = array();
    foreach($r as $row) {
      $stavy[$row['STAV_ID']] = array('nazev' => $row['NAZEV'], 'popis' => $row['POPIS']);
    }
    
    $this->stavy = $stavy;

  }

/*
SELECT S.STAV_ID, S.NAZEV, S.POPIS FROM UZIVATEL U 
		INNER JOIN UZIVATEL_ROLE UR ON UR.ID_UZIV = U.ID 
		INNER JOIN STAVY_PRECHOD SP ON SP.ID_ROLE = UR.ID_ROLE AND SP.ENTITA = %s 
		INNER JOIN STAV S ON S.STAV_ID = SP.ID_STAV_CIL AND S.ENTITA = SP.ENTITA AND S.POCATECNI = 1 
		WHERE U.ID = %i
 */
/**
 * Načtení počátečních stavů do $this->stavy
 * @param $key
 */
  public function newStates() {
    $registry = Registry::getInstance();
    $result = dibi::query('SELECT S.STAV_ID, S.NAZEV, S.POPIS FROM STAV S
    	WHERE S.ENTITA = %s AND S.POCATECNI = 1 ', $this->table );

    $r = $result->fetchAssoc('STAV_ID');
    $stavy = array();
    foreach($r as $row) {
      $stavy[$row['STAV_ID']] = array('nazev' => $row['NAZEV'], 'popis' => $row['POPIS']);
    }
    $this->stavy = $stavy;
  }

  public function stateDiffValidate() {
    $array = array();
    foreach($this->stavy as $key=>$value) {
      $array[] = $key;
    }

    switch (count($array)) {
      default:
      case 1:
        $diff1 = array_diff_assoc($this->state_validate[$array[0]],$this->valAll);
        $diff2 = array_diff_assoc($this->valAll,$this->state_validate[$array[0]]);
        $diff = array_merge($diff1, $diff2);
        break;
    
      case 2:
        $diff1 = array_diff_assoc($this->state_validate[$array[0]],$this->state_validate[$array[1]]);
        $diff2 = array_diff_assoc($this->state_validate[$array[1]],$this->state_validate[$array[0]]);
        $diff = array_merge($diff1, $diff2);  // rozdíly ve validaci mezi stavy
        break;
    
      case 3:
        $diff1 = array_diff_assoc($this->state_validate[$array[0]],$this->state_validate[$array[1]],$this->state_validate[$array[2]]);
        $diff2 = array_diff_assoc($this->state_validate[$array[1]],$this->state_validate[$array[2]],$this->state_validate[$array[0]]);
        $diff3 = array_diff_assoc($this->state_validate[$array[2]],$this->state_validate[$array[0]],$this->state_validate[$array[1]]);
        $diff = array_merge($diff1, $diff2, $diff3);  // rozdíly ve validaci mezi stavy
        break;
    }
    
    $new_validate = $this->validate;
    foreach($diff as $key=>$value) {
      unset($new_validate[$key]['required']);
      if (count($new_validate[$key]) == 0) {
        unset($new_validate[$key]);
      }
    }
    $this->validate = $new_validate;
  }

/**
 * Zpráva o změnách záznamu
 * @param $diff
 */
  protected function getDiffMess($diff) {
    $message = 'Záznam, který modifikujete byl běham vaší práce změněn jiným uživatelem.<br />
    	Pokud chcete přesto uložit změny, které jste provedly, odešlete formulář znovu.<br />
    	Seznam položek, které se liší:<br/><ul>';
    foreach ($diff as $item) {
      $message .= "<li>$item</li>";
    }
    $message .= '</ul>';
    Tools::setMessage($message, '', 1);
    $registry = Registry::getInstance();
    $_POST[HASH] = $this->hash;
    $registry->addPersistent('next_post',$_POST,9);
    
    $args = $registry->argsBack;
    $key = array_search(SEND,$args);  // zruší příznak SEND, pokud je nastaven        
    if ($key != false) {
      unset($args[$key]);
    }
    $url = implode(DIRSEP, $args);
    Tools::redirect($url);
    die;
  } 

/**
 * Vrací objekt jako pole
 * @param $upper
 * @param $atributy
 * @param $hash
 * @return array
 */
  public function toArray($upper = false, array $atributy = array(), $id = false) {

    $ret = array();
    
    if (count($atributy) == 0) {
      $atributy = $this->setter;
    }
    
    if ($id) {
    	$atributy[] = 'id';
    }

    if ($upper) {  // pokud je UPPER, jsou data připravena jako pro databázi, 
      foreach($atributy as $item) {
        //if (!empty($this->$item) OR (is_numeric($this->$item))) {
          $ret[strtoupper($item)] = $this->$item;
//        }
      }
    }
    else { // pokud ne, jsou připravana pro formulář (je přidán HASH)
      $atributy[] = HASH;
      foreach($atributy as $item) {
        if (!empty($this->$item)) {
          $ret[$item] = $this->$item;
        }
      }
    }

    return $ret;
  }
  
  
/**
 * Vrací objekt jako pole NN
 * @param $upper
 * @param $atributy
 * @param $hash
 * @return array
 */
  public function toArrayNN($upper = false, array $atributy = array(), $id = false) {

    $ret = array();
    
    if (count($atributy) == 0) {
      $atributy = $this->setter;
    }
    
    if ($id) {
    	$atributy[] = 'id';
    }

    if ($upper) {  // pokud je UPPER, jsou data připravena jako pro databázi, 
      foreach($atributy as $item) {
        if (!empty($this->$item) OR (is_numeric($this->$item))) {
          $ret[strtoupper($item)] = $this->$item;
        }
      }
    }
    else { // pokud ne, jsou připravana pro formulář (je přidán HASH)
      $atributy[] = HASH;
      foreach($atributy as $item) {
        if (!empty($this->$item)) {
          $ret[$item] = $this->$item;
        }
      }
    }

    return $ret;
  }

/**
 * Zpracování a uložení změny záznamu
 * @param $id
 * @param $form_id
 */
  public function upravit($id, $form_id, $state_nocheck = false) {

    $diff = $this->checkDiff($id,$form_id, $state_nocheck);  // false, pokud se nic nezměnilo
    if ( $diff != false ) {

      $this->getDiffMess($diff);

    }

    $this->setFromPost();
    $this->update();
    return true;

  }
  
  public function dbCols($cols) {
  	foreach($cols as $item){
  		if (array_search($item, $this->getter) === false) {
  			return false;
  		}
  	}
  	return true;
  }

  public function getAllQuery() {

  	$query = array();
  	$query[] = 'SELECT E.*, S.NAZEV AS S_NAZEV
		FROM ['.$this->table.'] AS E';
  	array_push($query, 'INNER JOIN [STAV] S ON S.STAV_ID = E.STAV AND S.ENTITA = %s',$this->table );
  	return $query;

  }

  public function getAll() {

  	$query = $this->getAllQuery();
    $res = dibi::query($query);
    return $res->fetchAssoc('ID');

  }

  public function delete($id) {

  	dibi::query('DELETE FROM ['.$this->table.'] WHERE ID = %i',$id);

  }

}
?>