<?php

/**
 * Does most of the dirty work in getting Database entries into
 * an object.
 * Takes an ID, or FronkDB table row for automatic loading
 * @author fronk
 * @param optional ID or table row $_
 */

if(!defined("FRONKDB")) {
  define("FRONKDB", true);
}

class mfBaseModel {
	public $id;
	public $data;
  public $_old_data;
	private $create;
	private $edit;
	private $worker;
	protected $forcestr;

  private $mode = "new";
  private $saved = 0;
  protected $db;
	protected $log;
	protected $table=false;
	protected $fieldprefix=false;

	/**
	 * Takes ID or DB row as arguments
	 * @param id or table row $_
	 */
	public function __construct($_=NULL) {
		$this->log=mfLoghandler::singleton();
		$this->table=get_class($this);
    $this->data = new stdClass();
    $this->_old_data = new stdClass();

		if(defined("MFMODEL_USEFIELDPREFIX") && MFMODEL_USEFIELDPREFIX==true) {
			$this->prefixfields=true;
		}
		if(method_exists($this, "init")) {
			$this->init($_);
		}
		$this->data = new stdClass();
    
    if(FRONKDB) {
        $this->db=FronkDB::singleton();
    }
    
		if(is_numeric($_)) {
      $this->fetch($_);
		} elseif(is_object($_)) {
			$this->load($_);
		}
	}

	public function load($row) {
		foreach($row as $field => $value) {
			if($this->fieldprefix) {
				if(preg_match('/^'.$this->fieldprefix.'_(.+)$/',$field,$m)) {
					$field=$m[1];
				}
			}
			if($field=="id"
				|| $field=="create"
				|| $field=="edit") continue;
			$this->data->$field=$value;
		}
    
		$this->id=$row->id;
		$this->create=$row->create;
		$this->edit=$row->edit;
		if($this->fieldprefix) {
			$prop=$this->fieldprefix."_id";
			$this->id=$row->$prop;
		}
    
    $this->_old_data = clone($this->data);
    $this->_old_data->id = $this->id;
    $this->_old_data->create = $this->create;
    $this->_old_data->edit = $this->edit;
    
    $this->mode = "update";
    
    if(method_exists($this, "afterLoad")) {
      $this->afterLoad();
    }
    
		return true;
	}

	public function fetch($id) {
		$where="id=$id";
		if($this->fieldprefix) {
			$where=$this->fieldprefix."_id=$id";
		}

		$res=$this->db->select($this->table,"*","$where");
		if($this->db->num_rows($res)) {
			$data=$this->db->fetch_object($res);
			$this->load($data);
			return true;
		}
		return false;
	}

  public function update(Array $data) {
    foreach($data as $key => $value) {
      if($value === null) {
        $this->$key = null;
      } else {
        $this->$key = $value;
      }
    }
  }
  
	public function save() {
    if(method_exists($this, "beforeSave")) {
      $this->beforeSave();
    }
		$fields=$this->buildFields();

    $forcestr = array();

    if(!$this->fieldprefix && is_array($this->forcestr) && count($this->forcestr)) {
      $forcestr = $this->forcestr;
    }

    if(is_array($this->forcestr) && count($this->forcestr)) {
      foreach($this->forcestr as $fstr) {
        $forcestr[] = $this->fieldprefix . "_$fstr";
      }
    }

		if(!is_array($fields) or !count($fields)) {
			return false;
		}
    
		if($this->id) {
			$id=$this->id;
			$where="`id`=$id";
			if($this->fieldprefix) {
				$where="`".$this->fieldprefix."_id`=$id";
			}
			if($this->db->update($this->table,$fields,$where,$forcestr)) {
        $this->saved++;
        if(method_exists($this, "afterSave")) {
          $this->afterSave();
        }
				return $id;
			}
		} else {
			if($this->db->insert($this->table,$fields,$forcestr)) {
				$id=mysqli_insert_id($this->db->link);
				$this->id=$id;
        $this->saved++;
        if(method_exists($this, "afterSave")) {
          $this->afterSave();
        }
				return $id;
			}
		}
		return false;
	}

	protected function buildFields() {
		$fields = array();
		foreach($this->data as $field => $value) {
			if($this->fieldprefix && !strstr($field,"_")) {
				$field = $this->fieldprefix."_$field";
			}
			$fields[$field]=$value;
		}

		if(!$this->create) {
			$this->create = date('U');
		}
		$this->edit = date('U');

		foreach(array("create","edit") as $f) {
			$fields[$f] = $this->$f;
		}

    if(method_exists($this, "afterBuildFields")) {
      $fields = $this->afterBuildFields($fields);
    }
    
		return $fields;
	}

	public function delete() {
		if($this->id) {
			$id=$this->id;
			$where="id=$id";
			if($this->fieldprefix && !strstr($field,"_")) {
				$where=$this->fieldprefix."_id=$id";
			}
			if($this->db->delete($this->table,$where)) {
        if(method_exists($this, "afterDelete")) {
          $this->afterDelete();
        }
				$this->data=new stdClass();
				$this->id="";
				return true;
			}
		}
		return false;
	}
  
  public function new() {
    if($this->mode == "new" && $this->saved <= 1) {
      return "new";
    } else {
      return "update";
    }
  }


	// generic functions for entity-classes
	public function __toString() {
		return (string) $this->data->Name;
	}

	public function __get($name) {
    if($name == "create" || $name == "edit") {
      if(isset($this->data->$name)) {
        return $this->data->$name;
      } else {
        return $this->$name;
      }
    }
    
    
		if(isset($this->data->$name)) {
			return $this->data->$name;
		} elseif(property_exists($this, $name)) {
      if(method_exists($this, "getProperty")) {
        $prop = $this->getProperty($name);
        if($prop === null){
          return null;
        }
        return $prop;
      } else {
        return $this->$name;
      }
		}
		return null;
	}

	public function __set($name,$value) {
		if($name=="create") {
				$this->create=intval($value);
		}
    $this->data->$name=$value;
		return true;
	}

	public function __unset($name) {
		unset($this->data->$name);
	}
  
  public function __debugInfo() {
    $vars = get_object_vars($this);
    if(is_object($vars['db'])) $vars['db'] = "object(FronkDB)";
    if(is_object($vars['log'])) $vars['log'] = 'object(mfLoghandler)';
    return $vars;
  }
}
