<?php
/**
 * general_driver.php - The driver for extends
 *
 * @author $Author: sathaporn $
 * @version $Id: general_driver.php,v 1.11 2010/06/30 06:38:07 sathaporn Exp $
 * @copyright Copyright (c) 2009, Tiller Software Co., Ltd.
*/
require_once (APPPATH . 'libraries/batch/driver_base.php');

class general_driver extends batch_driver_base{
	private $models;
	//This sql is shared in 2 methods so put it here
	public $user_sql;
	public $department_sql;
	public $course_sql;
	public $user_course_sql;
	public $course_grade_sql;
	
	//array property for fix value
	public $fix_department;
	//This structure field of department table.
	public $department;
	public $course;
	public $user;
	private $linebreak = '<br />';
	
	function __construct($config) {		
		if (!$config || !is_object($config)) {
		    die('<font color="red"><b>Could not Initial Config</b></font><br/>');
		}
		parent::__construct($config);
		
		if (!isset($config->models) || !file_exists('batch/'.$config->models)) {
			$this->CI->load->model('batch/'.$config->models);
			$this->models = $config->models;
		}
		
		$ret = false;
		if ($this->models) {
			echo '<font color="gray">Initial Driver : </font><font color="green"><b>Success</b></font><br/>';
			
			$func = $this->models;		
			$ret = $this->CI->$func->connect($config->db_server, $config->db_user, $config->db_password, $config->db_dbname); 
			
			if ($ret === FALSE)
    			die('Could not connect : <br/>' );
    		else
    			echo '<font color="gray">Connect : </font><font color="green"><b>Success</b></font><br/>';
		}
		else
			die('<font color="red"><b>Could not load model</b></font>');
			
		return $ret;
	}
	function __destruct() {
		if (($func = $this->models) !== FALSE) 
			$this->CI->$func->close();
	}
	protected function query($sql) {
		if (($func = $this->models) !== FALSE)
			return $this->CI->$func->query($sql);
		else 
			return false;	
	}
	private function update_db($insert_function, $update_function, $data_array, $field_id='id', $keyof_data_array='compare') {
		if ($insert_function === false || $insert_function == '')
			return false;
		if ($update_function === false || $update_function == '')
			return false;
		if (!is_array($data_array))
			return false;
		// delete by update flag enable
		if ($data_array['delete'] !== false && is_array($data_array['delete'])) {			 
			foreach($data_array['delete'] as $value) {
				if (array_key_exists($keyof_data_array, $value) && array_key_exists($field_id, $value[$keyof_data_array]) ) {
					$id = $value[$keyof_data_array][$field_id];
					if (is_numeric($id))
						$this->CI->cms_model->$update_function(false, $id, false);
				}
			}
		}
		// update data
		if ($data_array['update'] !== false && is_array($data_array['update'])) {			 
			foreach($data_array['update'] as $value) {
				if ($value && is_array($value) && 
					array_key_exists($keyof_data_array, $value) && array_key_exists($field_id, $value[$keyof_data_array]) &&
					$value['update'] && is_array($value['update']) && array_key_exists($keyof_data_array, $value['update']) ) {
					$id = false; 
					$id = $value[$keyof_data_array][$field_id];
			
					if (is_numeric($id) ) {
							$data = $value['update'][$keyof_data_array];	
							if ($data !== false) {
								$this->CI->cms_model->$update_function($data, $id, true);
							}
					}
				}
			}
		}
		
		// insert name only
		if ($data_array['insert'] !== false && is_array($data_array['insert'])) {			 
			foreach($data_array['insert'] as $value) {
				$data = array();
				if (array_key_exists($keyof_data_array, $value) ) {
					foreach($value[$keyof_data_array] as $key => $value)
						$data[$key] = $value;
				}
				if (!empty($data))
					$this->CI->cms_model->$insert_function($data);
			}
		}
		
		return true;
	}
	
	private function get_departments() {
		$departments = $this->CI->config->item('cms_data_sync_department');		
		$fields = $this->department['fields'];
		$ret = $this->mix_data($departments, $fields);
		
		return $ret;
	}
	function update_department() {
		$sis_departments = $this->get_departments();
		if (count($sis_departments) < $this->config_value('batch_min_departments')) {
			echo "Error, less than " . $this->config_value('batch_min_departments') . ", not processing any departments.\n";
			return FALSE;
		}
		$cms_departments = $this->CI->cms_model->get_departments();
		if (!$this->CI->config->item('cms_use_sync_department'))
			return FALSE;
		$ret = $this->process_data($sis_departments, $cms_departments, array('name'), 'md5');
				
		return $this->update_db('add_department','update_department',$ret);
	}	
	function get_courses() {
		$sql = $this->course_sql;		
		$courses = $this->query($sql);		
		$fields = $this->course['fields']; 
		$notnull = false;
		//$notnull = $this->course['notnull']; 
		
		$ret = $this->get_array($courses ,$fields,  $notnull);
		return $ret;
	}
	function update_courses() {
		$sis_courses = $this->get_courses();
		if (count($sis_courses) < $this->config_value('batch_min_courses')) {
			echo "Error, less than " . $this->config_value('batch_min_courses') . ", not processing any courses.\n";
			return FALSE;
		}
				
		$cms_departments = $this->CI->cms_model->get_departments();
		$sis_departments = $this->get_departments();
		
		//$sis_fix_data = $this->join_data($cms_departments, $sis_courses, 'name', 'departmentid', 'id');
		
		$departments = $this->join_data2($sis_departments, $cms_departments, 'name', 'name', 'shortname', 'shortname');
		$sis_fix_data = $this->join_data2($departments, $sis_courses, 'shortname', 'departmentid', 'id');
		
		$cms_courses = $this->CI->cms_model->get_courses();
		//$sis_fix_data = $this->join_data3($cms_courses, $sis_courses, 'sis_course_number', 'sis_course_number', 'autogen', 'autogen');
		//$sis_fix_data = $this->join_data3($cms_courses, $sis_courses, 'sis_course_number', 'sis_course_number', 'autogen', 'autogen');
		//$ret = $this->process_data($sis_fix_data, $cms_courses , array('sis_course_number','name', 'departmentid', 'autogen'), 'md5', false, 'sis_course_number');
		$ret = $this->process_data($sis_fix_data, $cms_courses , array('sis_course_dbid','sis_course_number','name', 'departmentid'), 'md5', false, 'sis_course_number');

		//echo '<pre>'. print_r(   $ret   ,true). '</pre>';
		
		return $this->update_db('add_course','update_course',$ret);
	}
	function get_users() {
		$sql = $this->user_sql;
		$users = $this->query($sql);
		$fields = $this->user['fields']; 
		//$notnull = $this->user['notnull']; 
		$notnull = false;
		$ret = $this->get_array($users ,$fields,  $notnull);
		return $ret;
	}
	function update_users() {
		$sis_users = $this->get_users();
		if (count($sis_users) < $this->config_value('batch_min_users')) {
			echo "Error, less than " . $this->config_value('batch_min_users') . ", not processing any users.\n";
			return FALSE;
		}
		$cms_users = $this->CI->cms_model->get_users();
		$cms_users = $this->hashit($cms_users, 'username');
		
		$sis_users = $this->hashit_user($sis_users, 'username');
		
		$fields = array('username','email','firstname','lastname','middlename','displayname','title','sis_teacher_number','auth_id','accessid','enabled','autogen','password');
		
		$ret = $this->process_data($sis_users, $cms_users , $fields, 'md5', false, 'username');
		
			/*'johns
		$temp_insert = array();
		foreach ($ret['insert'] as $insert) {
			$temp_insert[$insert['compare']['username']] = $insert;
		}
		$insert = array();
		foreach ($temp_insert as $row) {
			$insert[] = $row;
		}
		*/
		//$temp_insert = ksort($temp_insert);
			// echo '<pre>'. print_r( $cms_users ,true). '</pre>';
			 ///echo '<pre>'. print_r( $sis_users ,true). '</pre>';
			
		return $this->update_db('add_user','update_user',$ret);
	}
	function update_course_grade() {
		$sis_course_grade = $this->get_course_grade();
		if (count($sis_course_grade) < $this->config_value('batch_min_course_grades')) {
			echo "Error, less than " . $this->config_value('batch_min_course_grades') . ", not processing any course grades.\n";
			return FALSE;
		}
		
		echo 'update_course_grade<pre>'. print_r( $sis_course_grade ,true). '</pre>';
	}
	function update_user_course() {
		$sis_user_course = $this->get_user_course();
		if (count($sis_user_course) < $this->config_value('batch_min_user_course')) {
			echo "Error, less than " . $this->config_value('batch_min_user_course') . ", not processing any user course.\n";
			return FALSE;
		}
		/*
		$cms_user_course = $this->CI->cms_model->get_user_course();		
		$cms_users = $this->CI->cms_model->get_users();
		$sis_fix_data = $this->join_data2($cms_users, $sis_user_course, 'username', 'username', 'id', 'userid');		
		$cms_courses = $this->CI->cms_model->get_courses();
		$sis_fix_data = $this->join_data2($cms_courses, $sis_fix_data, 'sis_course_number', 'course_number', 'id', 'courseid');		 
		$ret = $this->hashit_user_course($sis_fix_data, 'courseid', 'userid');
		*/		  
		$cms_user_course = $this->CI->cms_model->get_user_course();
		$cms_users = $this->CI->cms_model->get_users();
		$sis_list = $this->hashit($sis_user_course, 'sis_courseid', 'username');
		$cms_list = $this->hashit($cms_user_course, 'sis_course_dbid', 'username');
		$user_list = $this->hashit($cms_users, 'username');
		$courses = $this->CI->cms_model->get_courses();
		$courses = $this->hashit($courses, 'sis_course_dbid');
		//var_dump($courses);
		//Loop thru the SIS result and compare to the CMS list to add to CMS
		//echo '<pre>'. print_r( $sis_user_course ,true). '</pre>';
		
		foreach ($sis_list as $key=>$item) {
			if ( array_key_exists($key, $cms_list) ) {
				continue;
			}
			$sis_courseid = $item['sis_courseid'];
			$username = $item['username'];
			if ( !array_key_exists($username, $user_list))
				continue; //Can't find the username in CMS so skip
			$cms_user = $user_list[$username];
			if ( $cms_user['enabled'] != 1 )
				continue; //User is disbaled so skip
			$cms_userid = $cms_user['id'];
			if (!array_key_exists($sis_courseid, $courses))
				continue; //No course with the same SIS db id in CMS so skip
			$cms_courseid = $courses[$sis_courseid];
			$cms_courseid = $cms_courseid['id'];
			if ( $this->CI->cms_model->add_user_course($cms_userid, $cms_courseid) )
				echo "Added userid: $cms_userid/$username, courseid: $cms_courseid to user_course.".$this->linebreak;
			else
				echo "Wrror, failed to add userid: $cms_userid/$username, courseid: $cms_courseid to user_course.".$this->linebreak;
		}
		//Loop thru the CMS result and compare to SIS to delete from CMS if not in SIS
		if ($cms_user_course !== false && is_array($cms_user_course)  ) {		
			foreach ($cms_list as $key=>$item) {		
				if ( !array_key_exists($key, $sis_list) ) {
					if ( $item['is_from_sis'] != 1 )
						continue; //It was added manually so ignore
					$userid = $item['userid'];
					$courseid = $item['courseid'];
					if ( $this->CI->cms_model->delete_user_course($userid, $courseid) )
						echo "Deleted userid: $userid, courseid: $courseid from user_course.".$this->linebreak;
					else
						echo "Error, failed to delete userid: $userid, courseid: $courseid from user_course.".$this->linebreak;
				}
			}
		}
		
		return true;
	}
	function get_user_course() {
		$sql = $this->user_course_sql;
		$users = $this->query($sql);
		$fields = $this->user_course['fields']; 
		//$notnull = $this->user['notnull']; 
		$notnull = false;
		$ret = $this->get_array($users ,$fields,  $notnull);
		return $ret;
	}

	function get_course_grade() {
		$sql = $this->course_grade_sql;
		//echo $sql .'<br />';
		return;
		/*
		$grade_trans = array(301=>15,302=>14,303=>13,304=>1,305=>2,306=>3,307=>4,
			308=>5,309=>6,309=>6,310=>7,311=>8,312=>9,313=>10,314=>11,315=>12);
		$sql = "SELECT cg.*,t.ENTRYID,t.DESCRIPTION  FROM EA7COURSEGRADELEVELS cg
				  JOIN TABLEENTRIES t ON t.TABLEENTRIESID=cg.GRADELEVEL";
		$coursegrade = $this->CI->mssql_model->query($sql);
		//var_dump($coursegrade); exit;
		if ( !$coursegrade || count($coursegrade) <= 0 )
			return FALSE;
		$ret = array();
		foreach ( $coursegrade as $item ) {
			$sis_grade_level = $item['GRADELEVEL'];
			//Can't find grade in translation, bad grade level in SIS, just skip
			if ( !array_key_exists($sis_grade_level, $grade_trans) )
				continue;
			$sis_grade_level = $grade_trans[$sis_grade_level];
			$ret[] = array('sis_courseid'=>$item['EA7COURSESID'], 'cms_gradeid'=>$sis_grade_level);
		}
		return $ret;
		*/
	}
		
	//detect data
	//comare all fields or some field for remove from add array or delete array
	function compare_data($needle, $haystack, $fix_data=false, $fix_key='compare') {
		if ($needle === FALSE || $haystack === FALSE)
			return FALSE;	
		if (!is_array($needle) || !is_array($haystack))
			return FALSE;
		if (empty($needle) || empty($haystack))
			return FALSE;		
					
		if ($fix_key !== false)
			$temp_needle = $needle[$fix_key];
		else
			$temp_needle = $needle;
			
		$temp = false;
		foreach($haystack as $key => $data) {
			if (array_key_exists($fix_key, $data) ) {			
				//uniq
				$found = false;
				$update = true;	
				//skip Update From SIS
				if (array_key_exists($fix_key, $data) && array_key_exists('autogen', $data[$fix_key]) && $data[$fix_key]['autogen'] == false) {
					$update = false;
					$temp[$key] = $data;
				}
				
				// fix compare data by field (fix_data)
				if ($fix_data !== false && !is_array($fix_data) && 
					$fix_key !== false && !is_array($fix_key) && 
					is_array($temp_needle) && is_array($data) && array_key_exists($fix_data, $temp_needle) &&
					array_key_exists($fix_key, $data) &&  array_key_exists($fix_data, $data[$fix_key]) && 
					$data[$fix_key][$fix_data] == $temp_needle[$fix_data]) {
					$found = true;
				}
				
				if ($found)
					$temp[$needle['md5']] = $needle;
							
				if ( $found && $update ) {
					if (array_key_exists('md5', $needle) ) {
						$temp[$needle['md5']] = $needle;
						$data['update'] = $needle;
					}
					$temp[$key] = $data;
				}
			}
		}
		return $temp;
	}
	
	//create new structure from array (sis, cms) 
	//check data from sis and cms by array_diff_key (key is md5 data by new structure)
	//return result to array insert, delete, update
	function process_data($sis_data, $cms_data, $indexs, $hashit=FALSE, $skip_process=FALSE, $fix_data=false, $fix_key='compare') {
		$sis = $this->new_structure($sis_data, $indexs, $hashit);//, $fix_data);
		$cms = $this->new_structure($cms_data, $indexs, $hashit);//, $fix_data);
		
		if ($sis === false)
			$sis = array();
		if ($cms === false)
			$cms = array();
			
		if (empty($sis) && $skip_process)
			return FALSE;
		
		$add = array();
		$del = array();
		$update = array();
		
		$add = array_diff_key($sis, $cms); //sis has data but cms don't has.
		$del = array_diff_key($cms, $sis); //cms more data but sis don't has.

		$res = array();
		foreach($add as $key => $row) {
			$temp = false;
			if (is_array($del) && !array_key_exists($key, $del) ) {
				$temp = $this->compare_data($row, $del, $fix_data, $fix_key);
			} 
			if ($temp !== false)
				$res[] = $temp;			
		}
		
		if (!empty($res)) {
			foreach($res as $same) {
					if ($same !== FALSE && is_array($same)) {
						$add = array_diff_key($add, $same); //remove data from add of array ,in case cms update but sis old data.
						$del = array_diff_key($del, $same); //remove data from delete of array ,in case cms update but sis old data.
					
					foreach($same as $sub_data) {
						if (array_key_exists('update', $sub_data) )
							$update[] = $sub_data;
					}
				}
			}
		}
		
		
		$result['insert'] = $add;
		$result['delete'] = $del;
		$result['update'] = $update;
		
		/* debug 
		echo '<table width="100%" border="1"><tr valign="top"><td><pre>'. print_r($add , true ) . '</pre></td>';
		echo '<td><pre>'. print_r($del , true ) . '</pre></td> ';	
	   echo '<td><pre>'. print_r($update , true ) . '</pre></td></tr></table>';
	   */
	   return $result;
	}
	
	
	//create new structure from array (sis, cms) 
	//create by fix fileds(indexs) to array['compare'] and merge all data then make short data by md5
	function new_structure($data, $indexs, $hashit=FALSE, $fix_data=FALSE) {
		if ($indexs === FALSE || $data === FALSE || !is_array($data) )
			return FALSE;
		$ret = FALSE;
		foreach($data as $key => $sis) {
			$md5 = false;
			$temp = array();
			foreach ($indexs as $index) {
				if (array_key_exists($index, $sis)) {
					//include id from data original
					if (array_key_exists('id', $sis))
						$temp['compare']['id'] = $sis['id'];
						
					$temp['compare'][$index] = $sis[$index];
					if ($fix_data != false) {
						if (!is_array($fix_data) && $fix_data == $index)
							$md5 .= $sis[$index];
					}
					elseif ($index != 'id') {
						$md5 .= $sis[$index];
					}
				}
			}
			if ($md5 !== false)
				$temp['md5'] = md5($md5);  // md5($md5);
			$ret[$key] = $temp;
		}
		if ($hashit !== FALSE)
			$ret = $this->hashit($ret, $hashit);			
		return $ret;
	}
	//Convert $data from sql to custom structure.
	private function get_array($data, $fileds=false, $notnull=false) {
		if ( !$data || count($data) <= 0 )
			return FALSE;
			
		if ( !is_array($fileds) || count($fileds) <= 0 )
			return FALSE;
						
		$ret = FALSE;
		foreach ($data as $row) {
			$skip_row = FALSE;
			$temp = array();
			foreach ($fileds as $field_name) {
				if ( array_key_exists($field_name, $row)) 
					$temp[$field_name] = $row[$field_name];
				if ($notnull !== FALSE && is_array($notnull) && in_array($field_name, $notnull) && 
					($row[$field_name] == null || $row[$field_name] == '') ) {
					$skip_row = true;
				}
			}
			if (!$skip_row)
				$ret[] = $temp;
		}
		//echo '<pre>'; print_r($ret); echo '</pre>';
		return $ret;
	}
	
	//Convert from custom structure to fix some value.
	function mix_data($data, $structure) {
		$res = $data;
		if (isset($structure) && $structure !== FALSE && is_array($data)) {			
			$temp = array();
			foreach ($data as $key => $value) {
				$rows = array();
				if (array_key_exists(0 , $structure))
					$rows[$structure[0]] = $key;
				if (array_key_exists(1 , $structure))
					$rows[$structure[1]] = $value;
				$temp[] = $rows;
			}
			$res = $temp;
		}
		return $res;
	}
	
	//Convert some value from Source array to Destination array with key.
	function join_data($data_source, $data_dest, $key_source, $key_dest, $get_source, $set_dest=FALSE) {
		if ($set_dest === FALSE) 
			$set_dest = $key_dest;
			
		$temp = $data_dest;
		if (isset($data_source) && $data_source !== FALSE && is_array($temp)) {
			foreach ($data_source as $values) {
				if (array_key_exists($key_source, $values) && array_key_exists($get_source, $values) ) {
					foreach ($data_dest as $key=> $rows) {
						if (array_key_exists($key_dest, $rows) && array_key_exists($set_dest, $rows) ) {
							if ($values[$key_source] === $rows[$key_dest]) {
								$temp[$key][$set_dest] =$values[$get_source];
							}
						}
					}
				}
			}
		}
		return $temp;
	}
	
	//Convert some value from Source array to Destination array with key.
	function join_data2($data_source, $data_dest, $key_source, $key_dest, $get_source, $set_dest=FALSE) {
		if ($set_dest === FALSE) 
			$set_dest = $key_dest;
		
		$temp = $data_dest;
		if (isset($data_source) && $data_source !== FALSE && is_array($temp)) {
			foreach ($data_source as $values) {
				if (array_key_exists($key_source, $values) && array_key_exists($get_source, $values) ) {						
					foreach ($data_dest as $key=> $rows) {
						if (array_key_exists($key_dest, $rows) ) {
							if ( is_array($values[$key_source]) && array_key_exists($set_dest, $rows) ) {								
								if (in_array($rows[$key_dest], $values[$key_source] )) 
									$temp[$key][$set_dest] = $values[$get_source];
							}
							else if ($values[$key_source] === $rows[$key_dest]) {
								$temp[$key][$set_dest] = $values[$get_source];
							}
						}
					}
				}
			}
		}
		
		//echo '<pre>'; print_r($temp ); echo '</pre>';
		
		return $temp;
	}
	//Convert some value from Source array to Destination array with key.
	function join_data3($data_source, $data_dest, $key_source, $key_dest, $get_source, $set_dest=FALSE) {
		if ($set_dest === FALSE) 
			$set_dest = $key_dest;
			
		$temp = $data_dest;
		if (isset($data_source) && $data_source !== FALSE && is_array($temp)) {
			foreach ($data_source as $values) {					
				if (array_key_exists($key_source, $values) && array_key_exists($get_source, $values) ) {
					foreach ($data_dest as $key=> $rows) {							
						if (array_key_exists($key_dest, $rows) && array_key_exists($set_dest, $rows) ) {
							
							if ( is_array($values[$key_source]) ) {
								
								
								if (in_array($rows[$key_dest], $values[$key_source] )) {
									//echo '<pre>'; print_r( $rows[$key_dest] ); echo '</pre>';
									$temp[$key][$set_dest] =$values[$get_source];
								}
							}
							else if ($values[$key_source] === $rows[$key_dest]) {
								$temp[$key][$set_dest] =$values[$get_source];
							}
						}
					}
				}
				else {
					echo 'join data key not found';
				}
			}
		}
		
		//echo '<pre>'; print_r($temp ); echo '</pre>';
		
		return $temp;
	}
	
	function hashit($array, $index, $index2='') {
		/*
		$ret=array();
		if ( !is_array($array) )
			return $ret;
		foreach($array as $item) {
			if ( $index2 == '' )
				$newindex = $item[$index];
			else
				$newindex = $item[$index] . '-' . $item[$index2];
				
			$newindex = strtolower($newindex);
			$ret[$newindex] = $item;
		}
		return $ret;
		*/
		
			//echo '<pre>'. print_r( $index ,true). '</pre>';
		
			//echo '<pre>'. print_r( $index2 ,true). '</pre>';
			
		$ret = array();				
		if ( !is_array($array) && !is_object($array) )
			return $array;		
		foreach($array as $item) {
			$newindex = false;
			if ( $index2 == '' && array_key_exists($index, $item)) {
				if (is_array($item)) 
					$newindex = $item[$index];
				elseif (is_object($item)) 
					$newindex = $item->$index;
			}		
			else {
				if (array_key_exists($index, $item) && array_key_exists($index2, $item) ) {
					if (is_array($item)) 
						$newindex = $item[$index] . '-' . $item[$index2];
					elseif (is_object($item)) 
						$newindex = $item->$index . '-' . $item->$index2;
				}
			}
			if ($newindex !== false) {
				$newindex = strtolower($newindex);
				$ret[$newindex] = $item;
			}
		}
		return $ret;
	}
	
	function hashit_user($array, $index, $index2='') {
		$ret = $this->hashit($array, $index, $index2);
		if ( !is_array($ret))
			return $array;	
		$temp = $ret;	
		foreach($ret as $key => $item) {
			if (!is_array($item))
				$item = (array)$item;
			$item['auth_id'] = $this->config_value('default_auth_id');
			$item['accessid'] = $this->config_value('default_access_id');
			$item['enabled'] = 1;
			$item['autogen'] = 1;
			$item['password'] = $this->config_value('ldap_access_pw');
			$temp[$key] = $item;		
		}
		return $temp;
	}
	
	function hashit_user_course($array, $index, $index2='') {
		$ret = $this->hashit($array, $index, $index2);
		if ( !is_array($ret))
			return $array;
		$temp = $ret;
		foreach($ret as $key => $item) {
			if (!is_array($item))
				$item = (array)$item;
			$item['iseditor'] = 1;
			$item['is_from_sis'] = 1;			
			$temp[$key] = $item;		
		}
		return $temp;
	}	
}
?>