<?php defined('SYSPATH') or die('No direct access allowed.');

/**
 * This class provides invoice information
 *
 * @package    Invoice
 * @category   Helpers
 * @author     Deon George
 * @copyright  (c) 2009-2013 Open Source Billing
 * @license    http://dev.osbill.net/license.html
 */
class Invoice {
	// This invoice Object
	private $_io;

	private $_methods = array(
		'dump',
		'save',
		'saved',
		'total',
	);

	// Enable passing calls to ORM::Invoice()
	public function __call($method,array $args) {
		if (in_array($method,$this->_methods))
			return call_user_func_array(array($this->_io,$method),$args);
		else
			throw HTTP_Exception::factory(501,'Unknown/unauthorised method :method',array(':method'=>$method));
	}

	public function __construct(Model_Invoice $io=NULL) {
		$this->_io = is_null($io) ? ORM::factory('Invoice') : $io;

		// Set our invoice as valid
		if (is_null($io))
			$this->_io->status = 1;
	}

	/**
	 * Add a Service to an Invoice
	 *
	 * @param $so Model_Servie
	 */
	public function add_service(Model_Service $so) {
		if ($this->_io->loaded())
			throw HTTP_Exception::factory(501,'Cannot use add service :service to an existing invoice :invoice ',array(':service'=>$so->id,':invoice'=>$this->_io->id));

		if (! $this->_io->account_id)
			$this->_io->account_id = $so->account_id;

		else if ($this->_io->account_id != $so->account_id)
			throw HTTP_Exception::factory(501,'Cannot add service :service to invoice - it is for a different account',array(':service'=>$so->id));

		// Set the invoice due date
		if (! $this->_io->due_date OR $this->_io->due_date > $this->min_due($so->date_next_invoice))
			$this->_io->due_date = $this->min_due($so->date_next_invoice);

		$pdata = Period::details($so->recur_schedule,$so->product->price_recurr_day,$so->invoiced_to()+86400,FALSE,$so->product->price_recurr_strict);

		$iio = ORM::factory('Invoice_Item');
		$iio->service_id = $so->id;
		$iio->module_id = $so->product->mid();
		$iio->module_ref = $so->product_id;
		$iio->quantity = $pdata['prorata'];
		$iio->price_base = $so->price();
		$iio->recurring_schedule = $so->recur_schedule;
		$iio->date_start = $pdata['start_time'];
		$iio->date_stop = $pdata['end_time'];

		// Service Billing
		$iio->item_type = 0;

		$this->_io->subitem_add($iio,$this->_io->account->country);

		// Check if there are any charges
		$c = ORM::factory('Charge')
			->where('service_id','=',$so->id)
			->where('void','is',NULL)
			->where('processed','is',NULL);

		foreach ($c->find_all() as $co) {
			$iio = ORM::factory('Invoice_Item');
			$iio->service_id = $co->service_id;
			$iio->module_id = $co->mid();
			$iio->module_ref = $co->id;
			$iio->quantity = $co->quantity;
			$iio->price_base = $co->amount;
			$iio->date_start = $co->date_orig;
			$iio->date_stop = $co->date_orig;
			$iio->item_type = $co->type;

			$this->_io->subitem_add($iio,$this->_io->account->country);
		}

		return $this;
	}

	/**
	 * Draw an invoice with a summary first page, and a detail subsequent pages
	 */
	private function draw_summary_invoice(Invoice_TCPDF $pdfo) {
		// Draw Invoice Basics
		$pdfo->drawCompanyLogo();
		$pdfo->drawCompanyAddress();
		$pdfo->drawInvoiceHeader();
		// @todo Get news from DB
		$pdfo->drawNews('');
		$pdfo->drawRemittenceStub();
		$pdfo->drawPaymentMethods();

		if ($this->_io->billing_status !=1 && $this->_io->due_date <= time())
			$pdfo->drawInvoiceDueNotice();
		elseif ($this->_io->billing_status == 1)
			$pdfo->drawInvoicePaidNotice();

		if ($this->_io->account->invoices_due_total())
			$pdfo->drawSummaryInvoicesDue();

		$pdfo->drawSummaryLineItems();

		// Next Page
		$pdfo->drawDetailLineItems();

		// Draw any Custom functions:
		$pdfo->drawCustom();
	}

	public static function instance(Model_Invoice $io=NULL) {
		return new Invoice($io);
	}

	public function min_due($date) {
		return strtotime(date('Y-M-d',($date < time()) ? time()+ORM::factory('Invoice')->config('DUE_DAYS_MIN')*86400 : $date));
	}

	public function render($type,$section,$args=array()) {
		$this->_io->pre_render();

		switch ($type) {
			case 'email':
				switch ($section) {
					case 'all':
						$token = ORM::factory('Module_Method_Token')
							->method(array('invoice','user_download'))
							->account($this->_io->account)
							->expire(time()+86400*21)
							->uses(3)
							->generate();

						$et = Email_Template::instance('task_invoice_send');
						$et->to = array('account'=>array($this->_io->account_id));
						$et->variables = array(
							'DUE'=>$this->_io->due(TRUE),
							'DUE_DATE'=>$this->_io->display('due_date'),
							'EMAIL'=>Company::instance()->email(),
							'FIRST_NAME'=>$this->_io->account->first_name,
							'HTML_INVOICE'=>View::factory('invoice/user/viewemail')->set('html',$this->render_html()),
							'INV_NUM'=>$this->_io->refnum(),
							'INV_URL'=>URL::site(URL::link('user','invoice/view/'.$this->_io->id),'http'),
							'INV_URL_DOWNLOAD'=>URL::site(URL::link('user',sprintf('invoice/download/%s?token=%s',$this->_io->id,$token)),'http'),
							'SITE_NAME'=>Company::instance()->name(),
						);

						return $et->send();

						break;

					default:
						throw HTTP_Exception::factory(501,'Unknown section type :section',array(':section'=>$section));
				}

				break;

			case 'html':
				switch ($section) {
					case 'body':
						return View::factory('invoice/user/view/body')
							->set('show_id',(isset($args['noid']) AND $args['noid']) ? FALSE : TRUE)
							->set('o',$this->_io);
						break;

					case 'all':
						return $this->render_html();

						break;

					default:
						throw HTTP_Exception::factory(501,'Unknown section type :section',array(':section'=>$section));
				}

				break;

			case 'pdf':
				switch ($section) {
					case 'all':
						if (isset($args['download']))
							return $this->render_pdf()->Output($args['download'],'D');
						else
							return $this->render_pdf();
						break;

					default:
						throw HTTP_Exception::factory(501,'Unknown section type :section',array(':section'=>$section));
				}

				break;

			default:
				throw HTTP_Exception::factory(501,'Unknown render type :type',array(':type'=>$type));
		}
	}

	/**
	 * Renders the invoice in HTML
	 */
	private function render_html() {
		return View::factory('invoice/user/view')
			->set('o',$this->_io);
	}

	/**
	 * Renders the invoice as a PDF and returns the PDF object
	 *
	 * @return Object Invoice_TCPDF
	 */
	private function render_pdf() {
		$class = Kohana::classname('Invoice_TCPDF_'.Kohana::$config->load('invoice')->driver);
		$pdfo = new $class($this->_io);

		if ($pdfo->getTemplate()) {
			$pagecount = $pdfo->setSourceFile($pdfo->getTemplate());
			$tplidx = $pdfo->ImportPage(1);
		}

		$pdfo->addPage();

		# If we are using FPDI
		if (isset($tplidx))
			$pdfo->useTemplate($tplidx);

		$this->draw_summary_invoice($pdfo);

		# If we get here, all is OK.
		return $pdfo;
	}
}
?>