<?php
/**
 * osBilling - Open Billing Software
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Originally authored by Deon George
 *
 * @author Deon George <deonATleenooksDOTnet>
 * @copyright 2009 Deon George
 * @link http://osb.leenooks.net
 * @license http://www.gnu.org/licenses/
 * @package AgileBill
 * @subpackage Modules
 */

/**
 * Module Abstract Class
 * This abstract class provides the basic variables and methods for all modules.
 *
 * @package AgileBill
 * @subpackage Modules
 */
abstract class OSB_module {
	# Validation error array
	public $val_error = array();

	/**
	 * Initialise the module
	 */
	public function __construct() {
		$f = sprintf('%s%s/%s_construct.xml',PATH_MODULES,get_class($this),get_class($this));

		if (is_file($f)) {
			# Open the construct file for parsing
			$C_xml = new CORE_xml;
			$construct = $C_xml->xml_to_array($f);

			# Required attributes of the construct file.
			foreach (array('module','table','field') as $index) {
				if (! isset($construct['construct'][$index]))
					printf('ERROR: Missing [%s] for [%s]',$index,$f);
				else
					$this->{$index} = $construct['construct'][$index];
			}

			# Optional attributes of the construct file.
			foreach (array('method','trigger','cache','order_by','limit','title','tpl') as $index)
				if (isset($construct['construct'][$index]))
					$this->{$index} = $construct['construct'][$index];

			# Bind our language translation path
			bindtextdomain($this->module,PATH_LANGUAGE);
			bind_textdomain_codeset($this->module,'UTF-8');
		}
	}

	public function getlist() {
		# Open the construct file for parsing
		$C_xml = new CORE_xml;

		$dh = opendir(PATH_MODULES);
		$modules = array();

		while ($module=readdir($dh))
			if (! in_array($module,array('.','..')) && is_dir(PATH_MODULES.$module) && is_file($f=sprintf('%s/%s/%s_install.xml',PATH_MODULES,$module,$module))) {

				$install = $C_xml->xml_to_array($f);

				# Determine the type
				if (! isset($install['install']['module_properties']['type']))
					$type = 'plugin';
				else
					$type = $install['install']['module_properties']['type'];

				# Store the data
				foreach (array('parent','name','notes','sub_modules','dependancy','type','table-only','method-only') as $index)
					if (isset($install['install']['module_properties'][$index]))
						$modules[$type][$module][$index] = $install['install']['module_properties'][$index];

			}

		return $modules;
	}

	/**
	 * Basic module helper when interacting with the database.
	 */
	private function database($type,$VAR,$method=null) {
		if ($type)
			$this->method[$type] = explode(',',$this->method[$type]);

		if (is_null($method) && $type)
			$method = $type;

		$db = new CORE_database;

		if (method_exists($db,$method))
			return $db->$method($VAR,$this,$type);

		else {
			global $C_debug;

			$C_debug->error(__FILE__,__METHOD__,sprintf('Method doesnt exist'));
		}
	}

	/**
	 * Export module helper
	 */
	private function export($type,$VAR,$method=null) {
		# Require the export class
		require_once(PATH_CORE.'export.inc.php');

		$this->method[$type] = explode(',',$this->method[$type]);

		if (is_null($method) && $type)
			$method = $type;

		$export = new CORE_export;

		if (method_exists($export,$method))
			$export->$method($VAR,$this,$type);

		else {
			global $C_debug;
			$C_debug->error(__FILE__,__METHOD__,sprintf('Method doesnt exist'));
		}
	}

	/** ADMIN INTERFACE METHODS **/

	/**
	 * Add Records to the database
	 */
	public function add($VAR) {
		if ($id = $this->database('add',$VAR))
			return $id;

		global $VAR;

		# If update fails, return to the add page.
		if (isset($VAR['_page']) && $VAR['_page'] == sprintf('%s:%s',$this->module,'view'))
			$VAR['_page'] = sprintf('%s:%s',$this->module,'add');

		return false;
	}

	/**
	 * Delete Records in the database
	 */
	public function delete($VAR) {
		return $this->database('',$VAR,'mass_delete');
	}

	/**
	 * Search for Records in the database
	 */
	public function search($VAR) {
		$this->database('search',$VAR);
	}

	/**
	 * Export Records from the database
	 */
	public function search_export($VAR) {
		switch ($VAR['format']) {
			case 'csv':
				$this->export('export_csv',$VAR,'search_csv');
				break;

			case 'excel':
				$this->export('export_excel',$VAR,'search_excel');
				break;

			case 'pdf':
				$this->export('export_pdf',$VAR,'pdf_invoice');
				break;

			case 'tab':
				$this->export('export_tab',$VAR,'search_tab');
				break;

			case 'xml':
				$this->export('export_xml',$VAR,'search_xml');
				break;

			default:
				global $C_debug;
				$C_debug->error(__FILE__,__METHOD__,sprintf('Export method doesnt exist'));
		}
	}

	/**
	 * Search for Records in the database
	 */
	public function search_form($VAR) {
		$this->database('search',$VAR,'search_form');
	}

	/**
	 * Show the Records from a Search in the database
	 */
	public function search_show($VAR) {
		return $this->database('search',$VAR,'search_show');
	}

	/**
	 * Update Records in the database
	 */
	public function update($VAR) {
		return $this->database('update',$VAR);
	}

	/**
	 * View a Record in the database
	 */
	public function view($VAR) {
		#@todo implement showing the modified attributes if a update fails.
		return $this->database('view',$VAR);
	}

	/** USER INTERFACE METHODS **/

	/**
	 * Add Records to the database
	 */
	public function user_add($VAR) {
		return $this->database('user_add',$VAR,'add');
	}

	/**
	 * Search for Records in the database
	 */
	public function user_search($VAR) {
		if (! SESS_LOGGED) return false;

		$VAR[$this->module.'_account_id'] = SESS_ACCOUNT;
		$this->database('search',$VAR);
	}

	/**
	 * Show the Records from a Search in the database
	 */
	public function user_search_show($VAR) {
		if (! SESS_LOGGED) return false;

		$this->database('search',$VAR,'search_show');
	}

	/**
	 * Static Vars
	 */
	public function static_var($VAR) {
		global $smarty;

		require_once(PATH_CORE.'static_var.inc.php');
		$static_var = new CORE_static_var;

		if (preg_match('/search/',$VAR['_page']))
			$arr = $static_var->generate_form($this->module,'add','search');
		else
			$arr = $static_var->generate_form($this->module,'add','update');

		if (gettype($arr) == 'array') {
			# Set everything as a smarty array, and return
			$smarty->assign('show_static_var',true);
			$smarty->assign('static_var',$arr);
			return true;

		} else {
			# Or if no results
			$smarty->assign('show_static_var',false);
			return false;
		}
	}

	/**
	 * Render the results of a search
	 *
	 * This function can also be used to only render the headers, and the tpl file can be responsible for rendering the data
	 *
	 * If $args is null, we only render the header
	 */
	public function tpl_search_show($VAR,$object,$args) {
		global $C_list;

		require_once(PATH_CORE.'translate.inc.php');
		$C_translate = new CORE_translate;

		list($class,$method) = explode(':',$VAR['_page']);

		if (! isset($this->tpl[$method]))
			return;

		# If we are only rendering the headers, then skip the table definitions
		if (! is_null($args)) {
			echo '<table width="100%" border="0" cellspacing="0" cellpadding="0" class="table_background"><tr><td>';
			echo '<table width="100%" border="0" cellspacing="1" cellpadding="3">';
		}

		# Display the search heading
		echo '<tr valign="middle" align="center" class="table_heading">';

		foreach ($this->tpl[$method] as $index => $details) {
			switch ($index) {
				case 'blank':
				case 'checkbox':
				case 'icon':
				case 'last':
					printf('<td class="table_heading"%s>&nbsp;</td>',isset($details['width']) ? sprintf(' style="width: %s;"',$details['width']) : '');
					break;

				default:
					printf('<td class="table_heading"%s>',isset($details['width']) ? sprintf(' style="width: %s;"',$details['width']) : '');
					printf('<script type="text/javascript">document.write(search_heading(\'%s\',\'%s\'));</script>',
						isset($details['translate']) ? $C_translate->translate($details['translate'],$this->module) : $C_translate->tf(array('module'=>$this->module,'field'=>$details['field']),null),
						$details['field']);
					echo '</td>';
			}
		}

		echo '</tr>';

		# Loop through each record
		if (is_array($args) && count($args[0])) {
			foreach ($args[0] as $key => $values) {
				printf('<tr id="row%s" onclick="row_sel(\'%s\',1);" ondblclick="window.location=\'?_page=%s:view&amp;id=%s,\';" onmouseover="row_mouseover(\'%s\',\'row_mouse_over_select\',\'row_mouse_over\');" onmouseout="row_mouseout(\'%s\',\'%s\',\'row_select\');" class="%s">',
					$values['id'],$values['id'],$this->module,$values['id'],$values['id'],$values['id'],$values['_C'],$values['_C']);

				foreach ($this->tpl[$method] as $index => $details) {
					switch ($details['type']) {
						case 'bool':
							printf('<td>%s</td>',$values[$details['field']] ? $C_translate->translate('true') : $C_translate->translate('false'));

							break;

						case 'bool_icon':
							echo '<td>';

							if ($values[$details['field']])
								printf('<img src="themes/%s/images/icons/%s" alt="True" width="16" height="16" style="border: 0px;"/>',DEFAULT_THEME,isset($details['true']) ? $details['true'] : 'go_16.gif');
							else
								printf('<img src="themes/%s/images/icons/%s" alt="False" width="16" height="16" style="border: 0px;"/>',DEFAULT_THEME,isset($details['false']) ? $details['false'] : 'stop_16.gif');

							echo '</td>';

							break;

						case 'checkbox':
							echo '<td style="text-align: center">';
							printf('<input type="checkbox" id="record%s" name="record%s" value="%s" onclick="row_sel(\'%s\',1,\'%s\');"/>',
								$values['id'],$values['id'],$values['id'],$values['id'],$values['_C']);
							printf('<script type="text/javascript">row_sel(\'%s\',0,\'%s\'); record_arr[i] = \'%s\'; i++;</script>',$values['id'],$values['_C'],$values['id']);
							echo '</td>';

							break;

						case 'currency':
							printf('<td>%s</td>',$C_list->format_currency($values[$details['field']],DEFAULT_CURRENCY));

							break;

						case 'date':
							printf('<td>%s %s</td>',date(UNIX_DATE_FORMAT,$values[$details['field']]),date(DEFAULT_TIME_FORMAT,$values[$details['field']]));

							break;

						case 'literal':
							printf('<td>%s</td>',str_replace('%%id%%',$values['id'],$details['data']));

							break;

						default:
							printf('<td>&nbsp;%s</td>',$values[$details['field']]);
					}
				}

				echo '</tr>';
			}
		}

		if (! is_null($args)) {
			echo '</table>';
			echo '</td></tr></table>';
		}
	}

	/**
	 * Get a record from the table by ID
	 */
	protected function getID($id) {
		$db = &DB();
		$record = $db->Execute(sqlSelect($db,$this->table,'*',array('id'=>$id)));

		if ($record && $record->RecordCount() == 1)
			return $record->fields;
		else
			return array();
	}

	/**
	 * Get a record from the table by Name
	 */
	protected function getName($name) {
		$db = &DB();
		$record = $db->Execute(sqlSelect($db,$this->table,'*',array('name'=>$name)));

		if ($record && $record->RecordCount() == 1)
			return $record->fields;
		else
			return array();
	}

	/**
	 * Graph Statistics
	 */
	public function graph($start,$end,$constraint,$default) {
		global $C_translate;

		$db = &DB();
		$result = $db->Execute(sqlSelect($db,$this->table,sprintf('date_orig>=%s AND date_orig<=',$start,$end)));

		if ($result->RecordCount() == 0) {
			$arr['title'] = $C_translate->translate('search_no_results','','');
			$arr['results'] = $default;

			return $arr;
		}

		while (! $result->EOF) {
			$d = $result->fields['date_orig'];

			for ($i=0; $i<count($constraint); $i++) {
				if ($d >= $constraint[$i]['start'] && $d < $constraint[$i]['end'])
					$default[$i]++;
			}

			$result->MoveNext();
		}

		$C_translate->value[$this->table]['count'] = $result->RecordCount();
		$title = $C_translate->translate('statistics',$this->table,'');
		$arr['title'] = $title;
		$arr['results'] = $default;

		return $arr;
	}
}
?>