<?php
/**
 * Product import
 *
 * @package		CSVIVirtueMart
 * @subpackage 	Import
 * @author 		Roland Dalmulder
 * @link 		http://www.csvimproved.com
 * @copyright 	Copyright (C) 2006 - 2012 RolandD Cyber Produksi
 * @license 	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 * @version 	$Id: productimport.php 1892 2012-02-11 11:01:09Z RolandD $
 */

defined( '_JEXEC' ) or die( 'Direct Access to this location is not allowed.' );

/**
 * Processor for product details
 *
 * Main processor for handling product details.
 *
 * @package	CSVIVirtueMart
 * @todo	Remove images
 * @todo	check update null fields
 */
class CsvivirtuemartModelProductimport extends CsvivirtuemartModelImportfile {

	// Private tables
	/** @var object contains the vm_product table */
	private $_vm_product = null;
	/** @var object contains the vm_price table */
	private $_vm_product_price = null;
	/** @var object contains the vm_product_discount table */
	private $_vm_product_discount = null;
	/** @var object contains the vm_product_attribute table */
	private $_vm_product_attribute = null;
	/** @var object contains the vm_product_attribute_sku table */
	private $_vm_product_attribute_sku = null;
	/** @var object contains the vm_product_relations table */
	private $_vm_product_relations = null;
	/** @var object contains the vm_manufacturer table */
	private $_vm_manufacturer = null;
	/** @var object contains the vm_mf_xref table */
	private $_vm_product_mf_xref = null;
	/** @var object contains the vm_tax_rate table */
	private $_vm_tax_rate = null;

	// Private variables
	/** @var mixed contains the data of the current field being processed */
	private $_datafield = null;
	/** @var array contains a list of categories the product belongs too */
	private $categories = null;
	/** @var bool contains the setting if we are dealing with a child product */
	private $child_product = false;
	/** @var string contains the creation date of a product */
	private $product_cdate = null;
	/** @var string contains the modified date of a product */
	private $product_mdate = null;
	/** @var object contains general import functions */
	private $_importmodel = null;
	/** @var object contains the category model */
	private $_categorymodel = null;

	// Public variables
	/** @var string contains the parent product atributes */
	public $attributes = null;
	/** @var integer contains the value for product boxes */
	public $product_box = null;
	/** @var integer contains the data for product packaging */
	public $product_packaging = null;
	/** @var string contains a list of related products separated by a | */
	public $related_products = null;
	/** @var array contains the setting if the product needs to be deleted */
	public $product_delete = null;
	/** @var integer contains the ID of the product parent */
	public $product_parent_id = null;
	/** @var bool contains the setting if the discount is a percentage or absolute value */
	public $is_percent = 0;
	/** @var string contains the category path for a product */
	public $category_path = null;
	/** @var integer contains the category ID for a product */
	public $category_id = null;
	/** @var integer contains the category IDs for a product from category_path */
	public $category_ids = null;
	/** @var integer contains the manufacturer ID */
	public $manufacturer_id = null;
	/** @var integer contains the tax value */
	public $product_tax = null;
	/** @var integer contains the tax ID */
	public $product_tax_id = null;
	/** @var integer contains the discount amount */
	public $amount = null;
	/** @var integer contains the discount start date */
	public $product_discount_date_start = null;
	/** @var integer contains the discount end date */
	public $product_discount_date_end = null;
	/** @var integer contains the product price */
	public $product_price = null;
	/** @var string contains the attribute values for a child product */
	public $attribute_values = null;
	/** @var string contains the parent SKU for a product */
	public $product_parent_sku = null;
	/** @var string contains the order in which the product is listed in the category */
	public $product_list = null;
	/** @var string contains the price including tax */
	public $price_with_tax = null;
	/** @var string contains the product currency */
	public $product_currency = null;
	/** @var int contains the shopper group id */
	public $shopper_group_id = null;
	/** @var int contains the discount id */
	public $product_discount_id = null;
	/** @var string contains product attributes including tax */
	public $attribute_with_tax = null;
	/** @var int contains the number of products in stock */
	public $product_in_stock = null;
	/** @var int contains the name of the full image */
	public $product_full_image = null;
	/** @var int contains the name of the thumbnail image */
	public $product_thumb_image = null;

	/**
	 * Constructor
	 *
	 * @copyright
	 * @author 		RolandD
	 * @todo
	 * @see
	 * @access 		public
	 * @param
	 * @return
	 * @since 		3.4
	 */
	public function __construct() {
		parent::__construct();
		// Load the tables that will contain the data
		$this->_loadTables();
		$this->loadSettings();
    }

    /**
     * Refresh the settings for XML nodes
     *
     * @copyright
     * @author 		RolandD
     * @todo
     * @see
     * @access 		protected
     * @param
     * @return
     * @since 		3.8.2
     */
	protected function refresh() {
		$this->loadSettings();
	}

	/**
	 * Here starts the processing
	 *
	 * @copyright
	 * @author 		RolandD
	 * @todo 		test downloadable files
	 * @todo 		add data read in case of incorrect columns.
	 * @todo		remove message about incorrect column count as import now ignores those???
	 * @see
	 * @access 		public
	 * @param
	 * @return
	 * @since 		3.0
	 */
	public function getStart() {
		// Load the data
		$this->loadData();

		// Get the logger
		$csvilog = JRequest::getVar('csvilog');

		$this->product_id = $this->getProductId();
		$this->vendor_id = $this->getVendorId();

		// Load ICEcat data if user wants to
		$this->getIcecat();

		// Process data
		foreach ($this->_csvifields as $name => $details) {
			if ($details['published']) {
				$this->_datafield = $this->validateInput($name);
				if ($this->_datafield !== false) {
					// Check if we are dealing with the last field
					if ($details == $this->_lastfield) $details['combine'] = false;

					// See if we are combining the field
					if ($details['combine']) $this->setCombineField($this->_datafield, $name);
					else {
						// Check if there are any fields to be combined
						if (!empty($this->combine_fields)) {
							// Get the fieldname the combine is for
							$name = $this->combine_settings['fieldname'];
							// Add the current data
							$this->setCombineField($this->_datafield);
							// Get the combined data
							$this->_datafield = $this->getCombineField();
						}

						// Check if the field needs extra treatment
						switch ($name) {
							case 'product_available_date':
								$this->$name = $this->convertDate($this->_datafield);
								break;
							case 'product_discount':
								$this->_productDiscount();
								break;
							case 'product_discount_date_start':
								$this->$name = $this->convertDate($this->_datafield);
								break;
							case 'product_discount_date_end':
								$this->$name = $this->convertDate($this->_datafield);
								break;
							case 'product_weight':
							case 'product_length':
							case 'product_width':
							case 'product_height':
								$this->$name = $this->toPeriod($this->_datafield);
								break;
							case 'shopper_group_name':
								$this->getShopperGroupName($this->_datafield);
								break;
							case 'related_products':
								if (substr($this->_datafield, -1, 1) == "|") $this->related_products = substr($this->_datafield, 0, -1);
								else $this->related_products = $this->_datafield;
								break;
							case 'product_cdate':
								$this->$name = $this->convertDate($this->_datafield);
								break;
							case 'product_mdate':
								$this->$name = $this->convertDate($this->_datafield);
								break;
							case 'category_id':
							case 'category_path':
								if (strlen(trim($this->_datafield)) > 0) {
									if (stripos($this->_datafield, '|') > 0) $category_ids[$name] = explode("|", $this->_datafield);
									else $category_ids[$name][] = $this->_datafield;
									$this->category_ids = $category_ids;
								}
								$this->$name = $this->_datafield;
								break;
							case 'manufacturer_name':
								$this->$name = $this->mf_name = $this->_datafield;
								break;
							case 'product_tax':
								if (!empty($this->_datafield)) $this->$name = $this->cleanPrice($this->_datafield);
								break;
							case 'price_with_tax':
								$this->$name = $this->cleanPrice($this->_datafield);
								break;
							case 'quantity_options':
								$this->$name = strtolower($this->_datafield);
								break;
							case 'product_publish':
								$allowed = array('Y', 'y', 'N', 'n');
								$this->$name = (in_array($this->_datafield, $allowed)) ? strtoupper($this->_datafield) : 'Y';
								break;
							case 'product_currency':
								$this->$name = strtoupper($this->_datafield);
								break;
							default:
								$this->$name = $this->_datafield;
								break;
						}
					}
				}
				else return false;
			}
		}

		// Calculate product packaging
		if (!is_null($this->product_box) && !is_null($this->product_packaging)) $this->_productPackaging();

		// We need the currency
		if (is_null($this->product_currency) && (isset($this->product_price) || isset($this->price_with_tax))) {
			$this->product_currency = $this->productCurrency($this->vendor_id);
		}

		// Check for child product and get parent SKU if it is
		if (!is_null($this->product_parent_sku)) {
			$this->_productParentSku();
		}

		// Set the record identifier
		$this->record_identity = (isset($this->product_sku)) ? $this->product_sku : $this->product_id;

		return true;
	}

	/**
	 * Process each record and store it in the database
	 *
	 * @copyright
	 * @author 		RolandD
	 * @todo
	 * @see
	 * @access 		public
	 * @param
	 * @return
	 * @since 		3.0
	 */
	public function getProcessRecord() {
		$db = JFactory::getDBO();
		$csvilog = JRequest::getVar('csvilog');
		$template = JRequest::getVar('template');

		if ($this->product_id && !$template->getValue('overwrite_existing_data', 'general')) {
		   $csvilog->addDebug(JText::sprintf('COM_CSVIVIRTUEMART_DATA_EXISTS_PRODUCT_SKU', $this->product_sku));
		   $csvilog->AddStats('skipped', JText::sprintf('COM_CSVIVIRTUEMART_DATA_EXISTS_PRODUCT_SKU', $this->product_sku));
		}
		else {
			if (empty($this->product_sku) && empty($this->product_id)) {
				$csvilog->AddStats('incorrect', JText::_('COM_CSVIVIRTUEMART_DEBUG_NO_SKU'));
				$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_NO_SKU_OR_ID'));
				return false;
			}
			else {
				$csvilog->addDebug(JText::sprintf('COM_CSVIVIRTUEMART_DEBUG_PROCESS_SKU', $this->record_identity));
			}

			// User wants to delete the product
			if (isset($this->product_id) && $this->product_delete == "Y") {
				$this->_vm_product->bind($this);
				// Delete the product
				if ($this->_vm_product->delete($this->product_id)) {
					// Delete category reference
					$q = "DELETE FROM #__vm_product_category_xref WHERE product_id = ".$this->product_id;
					$db->setQuery($q);
					$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_DELETE_CATEGORY_XREF'), true);
					$db->query();
					// Delete product type reference
					$q = "DELETE FROM #__vm_product_product_type_xref WHERE product_id = ".$this->product_id;
					$db->setQuery($q);
					$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_DELETE_PRODUCT_TYPE_XREF'), true);
					$db->query();
					// Delete manufacturer reference
					$q = "DELETE FROM #__vm_product_mf_xref WHERE product_id = ".$this->product_id;
					$db->setQuery($q);
					$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_DELETE_MANUFACTURER_XREF'), true);
					$db->query();
					// Delete child parent reference
					$q = "UPDATE #__vm_product SET product_parent_id = 0 WHERE product_parent_id = ".$this->product_id;
					$db->setQuery($q);
					$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_DELETE_PRODUCT_PARENT'), true);
					$db->query();
				}
			}
			else if (!isset($this->product_id) && $this->product_delete == "Y") {
				$csvilog->AddStats('skipped', JText::sprintf('COM_CSVIVIRTUEMART_NO_PRODUCT_ID_NO_DELETE', $this->record_identity));
			}
			else if (!isset($this->product_id) && $template->getValue('ignore_non_exist', 'general')) {
				// Do nothing for new products when user chooses to ignore new products
				$csvilog->AddStats('skipped', JText::sprintf('COM_CSVIVIRTUEMART_DATA_EXISTS_IGNORE_NEW', $this->record_identity));
			}
			// User wants to add or update the product
			else {
				// Check if any image handling needs to be done
				if ($template->getValue('process_image', 'image', false)) {
					if (!is_null($this->product_full_image) || $template->getValue('auto_generate_image_name', 'image', false)) {
						if (is_null($this->product_thumb_image)) $this->product_thumb_image = basename($this->product_full_image);
						// Image handling
						$imagehelper = new ImageHelper;
						// Generate image names
						if ($template->getValue('auto_generate_image_name', 'image')) {
							if ($this->_createImageName()) {
								$file_details = $imagehelper->ProcessImage($this->product_full_image, $this->product_thumb_image, $template->getValue('file_location_product_images', 'path'), $this->product_full_image_output, $this->product_thumb_image_output);
							}
						}
						else {
							$file_details = $imagehelper->ProcessImage($this->product_full_image, $this->product_thumb_image, $template->getValue('file_location_product_images', 'path'));
						}

						// Process the file details
						if ($file_details['full_image']['exists'] && $file_details['full_image']['isimage']) {
							$this->product_full_image = (empty($file_details['full_image']['output_folder'])) ? $file_details['full_image']['output_name'] : $file_details['full_image']['output_folder'].'/'.$file_details['full_image']['output_name'];
							$this->product_thumb_image = $file_details['thumb_image']['output_folder'].'/'.$file_details['thumb_image']['output_name'];
						}
						// See if we need to empty the image data
						else if ($template->getValue('empty_image', 'image')) {
							$this->product_full_image = '';
							$this->product_thumb_image = '';
						}

					}
				}

				// Process discount
				if (!is_null($this->amount)) $this->_processDiscount();

				// Process tax
				if (!empty($this->product_tax)) $this->_processTax();

				// Process manufacturer
				if (isset($this->mf_name) && $this->mf_name) $this->_manufacturerImport();
				else if (!isset($this->manufacturer_name) && !isset($this->manufacturer_id)) {
					// User is not importing manufacturer data but we need a default manufacturer associated with the product
					$this->_getDefaultManufacturerID();
				}

				// Process product info
				if ($this->_productQuery()) {
					// Check if the price is to be updated
					if (isset($this->product_price) || isset($this->price_with_tax)) $this->_priceQuery();

					// Handle child product attributes
					if ($this->attributes) $this->_processAttributes();

					// Handle child product attribute values */
					if ($this->attribute_values) $this->_processAttributeValues();

					// Handle manufacturer data
					if ((isset($this->manufacturer_id) && $this->manufacturer_id)) {
						// Add a product <--> manufacturer cross reference
						$this->_manufacturerCrossReference();
					}

					// Process related products
					// Related products are first input in the database as SKU
					// At the end of the import, this is converted to product ID
					if ($this->related_products) $this->_processRelatedProducts();


					// Process category path
					if (isset($this->category_path) || isset($this->category_id)) {
						if (($this->category_ids && !$this->child_product) || $this->category_id) {
							if (is_null($this->_categorymodel)) $this->_categorymodel = new CsvivirtuemartModelCategory();
							$this->_categorymodel->getStart();
							// Check the categories
							// Do we have IDs
							if (array_key_exists('category_id', $this->category_ids)) {
								$this->_categorymodel->CheckCategoryPath($this->product_id, false, $this->category_ids['category_id'], $this->product_list);
							}
							else if (array_key_exists('category_path', $this->category_ids)) {
								$this->_categorymodel->CheckCategoryPath($this->product_id, $this->category_ids['category_path'], false, $this->product_list);
							}

						}
						else if ($this->child_product) {
							$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_CHILD_PRODUCT_NO_HANDLING'));
						}
					}

					// Process product type names
					// for a product type called prod with fields one, two, three it would create fields producttype:field:value,producttype:field:value
					if (!empty($this->producttypenames)) {
						// Get the names
						if (strpos($this->producttypenames, '|') !== false) {
							$names = explode('|', $this->producttypenames);
						}
						else $names = array($this->producttypenames);

						// Process each name
						$details = array();
						foreach ($names as $nkey => $name) {
							list($product_type_name, $field, $value) = explode(':', $name);

							// Get the product type ID
							$product_type_id = $this->getProductTypeId($product_type_name);
							if ($product_type_id) {
								// Collect all details
								$details[$product_type_id][$field] = $value;
							}
							else {
								$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_PRODUCT_TYPE_ID_NOT_FOUND'), true);
								$csvilog->AddStats('incorrect', JText::sprintf('COM_CSVIVIRTUEMART_PRODUCT_TYPE_ID_NOT_FOUND', $product_type_name));
							}
						}

						// Process all found product type names
						foreach ($details as $product_type_id => $detail) {
							// Create the table name
							$tbl_name = '#__vm_product_type_'.$product_type_id;

							// Check if the current table is the same as the new one
							if ($tbl_name != $this->_vm_product_type_x->getValue('_tbl')) {
								// Set the table
								$this->_vm_product_type_x->setValue('_tbl', $tbl_name);
								// Set to reload
								$this->_vm_product_type_x->setValue('_loaded', false);
							}
							// Reset the fields
							$this->_vm_product_type_x->reset();

							// Bind the data
							$this->_vm_product_type_x->setValue('details', $detail);
							$this->_vm_product_type_x->setValue('product_id', $this->product_id);
							$this->_vm_product_type_x->setValue('product_type_id', $product_type_id);
							$identify = (isset($this->product_sku)) ? $this->product_sku : $this->product_id;
							$this->_vm_product_type_x->setValue('identify', $identify);

							// Store the data
							$this->_vm_product_type_x->store();

							// Bind the data for cross reference
							$this->_vm_product_product_type_xref->bind(array('product_id' => $this->product_id, 'product_type_id' => $product_type_id));
							// Add the cross reference
							$this->_vm_product_product_type_xref->store();
						}
					}
				}
			}
			// Now that all is done, we need to clean the table objects
			$this->cleanTables();
		}
	}

	/**
   	 * Execute any processes to finalize the import
   	 *
   	 * @copyright
   	 * @author 		RolandD
   	 * @todo
   	 * @see
   	 * @access 		public
   	 * @param 		array	$fields	list of fields used for import
   	 * @return
   	 * @since 		3.0
   	 */
	public function getPostProcessing($fields=array()) {
		// Related products
		if (in_array('related_products', $fields)) $this->_postProcessRelatedProducts();
	}

	/**
	 * Load the product related tables
	 *
	 * @copyright
	 * @author		RolandD
	 * @todo
	 * @see
	 * @access 		private
	 * @param
	 * @return
	 * @since 		3.0
	 */
	private function _loadTables() {
		$this->_vm_product = $this->getTable('vm_product');
		$this->_vm_product_price = $this->getTable('vm_product_price');
		$this->_vm_product_discount = $this->getTable('vm_product_discount');
		$this->_vm_product_attribute = $this->getTable('vm_product_attribute');
		$this->_vm_product_attribute_sku = $this->getTable('vm_product_attribute_sku');
		$this->_vm_product_relations = $this->getTable('vm_product_relations');
		$this->_vm_manufacturer = $this->getTable('vm_manufacturer');
		$this->_vm_product_mf_xref = $this->getTable('vm_product_mf_xref');
		$this->_vm_tax_rate = $this->getTable('vm_tax_rate');
		$this->_vm_product_type_x = $this->getTable('vm_product_type_x');
		$this->_vm_product_product_type_xref = $this->getTable('vm_product_product_type_xref');
	}

	/**
	 * Cleaning the product related tables
	 *
	 * @copyright
	 * @author 		RolandD
	 * @todo
	 * @see
	 * @access 		protected
	 * @param
	 * @return
	 * @since 		3.0
	 */
	protected function cleanTables() {
		$this->_vm_product->reset();
		$this->_vm_product_price->reset();
		$this->_vm_product_discount->reset();
		$this->_vm_product_attribute->reset();
		$this->_vm_product_attribute_sku->reset();
		$this->_vm_product_relations->reset();
		$this->_vm_manufacturer->reset();
		$this->_vm_product_mf_xref->reset();
		$this->_vm_tax_rate->reset();
		$this->_vm_product_product_type_xref->reset();

		// Clean local variables
		$class_vars = get_class_vars(get_class($this));
		foreach ($class_vars as $name => $value) {
			if (substr($name, 0, 1) != '_') {
				$this->$name = $value;
			}
		}
	}

	/**
	 * Get the product discount
	 *
	 * Replace commas with periods for correct DB insertion of the product price
	 *
	 * @copyright
	 * @author 		RolandD
	 * @todo
	 * @see
	 * @access 		private
	 * @param
	 * @return
	 * @since 		3.0
	 */
	private function _productDiscount() {
		// Determine if the discount field is a percentage
		if ($this->_datafield) {
			if (substr($this->_datafield,-1,1) == "%") $this->is_percent = "1";
		}

		// Set the discount amount
		if ($this->is_percent) {
			$this->amount = substr($this->toPeriod($this->_datafield), 0, -1);
		}
		else $this->amount = $this->cleanPrice($this->_datafield);
	}

	/**
	 * Get the product packaging
	 *
	 * The number is calculated by hexnumbers
	 *
	 * @copyright
	 * @author		RolandD
	 * @todo
	 * @see
	 * @access 		private
	 * @param
	 * @return
	 * @since 		3.0
	 */
	private function _productPackaging() {
		$this->product_packaging = (($this->product_box<<16) | ($this->product_packaging & 0xFFFF));
	}

	/**
	 * Get the product parent sku if it is a child product
	 *
	 * The parent product MUST be imported before the child product
	 *
	 * @copyright
	 * @author		RolandD
	 * @todo
	 * @see
	 * @access 		private
	 * @param
	 * @return
	 * @since 		3.0
	 */
	private function _productParentSku() {
		$db = JFactory::getDBO();
		$csvilog = JRequest::getVar('csvilog');
		$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_PRODUCT_PARENT_SKU'));
		if (!$this->category_path && isset($this->product_sku)) {
			// Check if we are dealing with a child product
			if ($this->product_parent_sku !== $this->product_sku) {
				$this->child_product = true;
				// Get the parent id first
				$db->setQuery("SELECT product_id FROM #__vm_product WHERE product_sku = '".$this->product_parent_sku."'");
				$this->product_parent_id = $db->loadResult();
				$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_PRODUCT_PARENT_SKU'), true);
			}
			else {
				$this->product_parent_id = 0;
				$this->child_product = false;
			}
		}
	}

	/**
	 * Creates either an update or insert SQL query for a product.
	 *
	 * @copyright
	 * @author 		RolandD
	 * @todo
	 * @see
	 * @access 		private
	 * @param
	 * @return 		bool true if the query executed successful|false if the query failed
	 * @since 		3.0
	 */
	private function _productQuery() {
		$csvilog = JRequest::getVar('csvilog');

		// Load the current product data
		$this->_vm_product->load($this->product_id);

		// Remove the tax of the attribute if needed
		if ($this->product_tax && $this->attribute_with_tax) {
			if (strlen($this->attribute_with_tax) == 0) $this->attribute = null;
			else {
				$this->attribute = $this->attribute_with_tax;
				$matches = array();
				$pattern = '/\[.*?]/';
				// Get all matches
				preg_match_all($pattern, $this->attribute_with_tax, $matches);

				$find = array('[', ']', '+', '-', '=');
				foreach ($matches[0] as $key => $match) {
					// Remove all obsolete characters
					$attribute_price = str_replace($find, '', $match);
					// Remove the tax
					$this->attribute = str_replace($match, str_replace($attribute_price, number_format($attribute_price / (1+($this->product_tax/100)), 5, '.', ''), $match), $this->attribute);
				}
			}
		}

		// Check if we need to do a stock calculation
		if (!is_null($this->product_in_stock)) {
			// Split the modification
			$operation = substr($this->product_in_stock, 0, 1);
			$value = substr($this->product_in_stock, 1);

			// Get the database value
			$stock = $this->_vm_product->product_in_stock;

			// Check what modification we need to do and apply it
			switch ($operation) {
				case '+':
					$stock += $value;
					break;
				case '-':
					$stock -= $value;
					break;
				case '/':
					$stock /= $value;
					break;
				case '*':
					$stock*= $value;
					break;
				default:
					// Assign the current price to prevent it being overwritten
					$stock = $this->product_in_stock;
					break;
			}
			$this->product_in_stock = $stock;
		}

		// Bind the initial data
		$this->_vm_product->bind($this);

		// Set some initial values
		// Set the modified date as we are modifying the product
		if (!isset($this->mdate)) $this->_vm_product->mdate = time();

		// Add a creating date if there is no product_id
		if (is_null($this->product_id)) $this->_vm_product->cdate = time();
		foreach ($this->_avfields as $id => $column) {
			// Only process the fields the user is uploading
			if (isset($this->$column)) {
				// Add a redirect for the product cdate
				if ($column == "product_cdate" && !empty($this->$column)) {
					$this->_vm_product->cdate = $this->$column;
				}

				// Add a redirect for the product mdate
				if ($column == "product_mdate" && !empty($this->$column)) {
					$this->_vm_product->mdate = $this->$column;
				}
			}
		}

		// We have a succesful save, get the product_id
		if ($this->_vm_product->store()) {
			if ($this->queryResult() == 'UPDATE') $csvilog->AddStats('updated', JText::_('COM_CSVIVIRTUEMART_UPDATE_PRODUCT_SKU'));
			else $csvilog->AddStats('added', JText::_('COM_CSVIVIRTUEMART_ADD_PRODUCT_SKU'));
		}
		else {
			$csvilog->AddStats('incorrect', JText::sprintf('COM_CSVIVIRTUEMART_PRODUCT_NOT_ADDED', $this->_vm_product->getError()));
			return false;
		}

		// Store the debug message
		$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_PRODUCT_QUERY'), true);

		// Set the product ID
		$this->product_id = $this->_vm_product->product_id;

		// All good
		return true;
	}

    /**
     * Process Related Products
     *
     * @copyright
     * @author		RolandD
     * @todo
     * @see
     * @access 		private
     * @param
     * @return
     * @since 		3.0
     */
	private function _processRelatedProducts() {
		$db = JFactory::getDBO();
		$relatedproducts = explode("|", $this->related_products);

		foreach ($relatedproducts AS $key => $relatedproduct) {
			$q = "INSERT INTO ".$db->nameQuote('#__csvivirtuemart_related_products')."
				VALUES (".$db->Quote($this->product_sku).", ".$db->Quote($relatedproduct).")";
				$db->setQuery($q);
				$db->query();
		}
   }

   /**
    * Post Process Related Products
    *
    * @copyright
    * @author		RolandD
    * @todo
    * @see
    * @access 		private
    * @param
    * @return
    * @since 		3.0
    */
   private function _postProcessRelatedProducts() {
	  $db = JFactory::getDBO();
	  $csvilog = JRequest::getVar('csvilog');
	  $relations = array();
	  // Get the related products
	  $q = "SELECT p1.product_id, GROUP_CONCAT(p2.product_id SEPARATOR '|') AS related_products
			FROM #__csvivirtuemart_related_products r
			LEFT JOIN #__vm_product p1
			ON r.product_sku = p1.product_sku
			LEFT JOIN #__vm_product p2
			ON r.related_sku = p2.product_sku
			GROUP BY r.product_sku";
	  $db->setQuery($q);
	  $csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_PROCESS_RELATED_PRODUCTS'), true);
	  $relations = $db->loadObjectList();
	  if (!empty($relations)) {
		  foreach ($relations as $key => $related) {
			  $this->_vm_product_relations->bind($related);
			  if ($this->_vm_product_relations->store()) {
			  	  $csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_PROCESS_RELATED_PRODUCTS'), true);
			  }
			  else {
				  $csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_RELATED_PRODUCTS'), true);
			  }
		  }

		  // Empty the relations table
		  $db->setQuery("TRUNCATE ".$db->nameQuote('#__csvivirtuemart_related_products'));
		  $db->query();

	  }
	  else {
		  $csvilog->AddStats('incorrect', JText::_('COM_CSVIVIRTUEMART_NO_RELATED_PRODUCTS_FOUND'), true);
	  }
   }

   /**
    * Process tax fields
    *
    * @copyright
    * @author 		RolandD
    * @todo 		Give user the option to use all fields (tax_state, tax_country)
	* @todo 		Add logging
    * @see
    * @access 		private
    * @param
    * @return
    * @since 		3.0
    */
    private function _processTax() {
    	$db = JFactory::getDBO();
    	$csvilog = JRequest::getVar('csvilog');

    	// Set the tax state
    	$this->tax_state = '-';
    	// Set the default tax country
    	$q = "SELECT vendor_country
    		FROM #__vm_vendor
    		WHERE vendor_id = 1;";
    	$db->setQuery($q);
    	$this->tax_country = $db->loadResult();

		// Assign the values
		$this->_vm_tax_rate->bind($this);

		// Set some custom values
		if (isset($this->product_tax_id)) $this->_vm_tax_rate->setValue('tax_rate_id', $this->product_tax_id);
		else if (isset($this->product_tax)) {
			$tax_rate = ($this->product_tax/100);
			$this->_vm_tax_rate->setValue('tax_rate', $tax_rate);

			// Check for tax rate id
			$q = "SELECT tax_rate_id
				FROM #__vm_tax_rate
				WHERE tax_rate='".$tax_rate."'";
			$db->setQuery($q);
			$tax_rate_id = $db->loadResult();
			$this->_vm_tax_rate->setValue('tax_rate_id', $tax_rate_id);
		}
		$this->_vm_tax_rate->setValue('mdate', time());
		if ($this->_vm_tax_rate->store()) {
			// Now we have a product tax ID
			$this->product_tax_id = $this->_vm_tax_rate->getValue('tax_rate_id');
		}
		$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_PROCESS_TAX'), true);
	}

	/**
	 * Manufacturer Importer
	 *
	 * Adds or updates a manufacturer and adds a reference to the product
	 *
	 * @copyright
	 * @author		RolandD
	 * @todo
	 * @see
	 * @access 		private
	 * @param
	 * @return
	 * @since 		3.0
	 */
	private function _manufacturerImport() {
		$csvilog = JRequest::getVar('csvilog');
		$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_MANUFACTURER_IMPORT'));
		$this->_vm_manufacturer->bind($this);
		if ($this->_vm_manufacturer->store()) {
			if ($this->queryResult() == 'UPDATE') $csvilog->AddStats('updated', JText::_('COM_CSVIVIRTUEMART_UPDATE_MANUFACTURER'));
			else $csvilog->AddStats('added', JText::_('COM_CSVIVIRTUEMART_ADD_MANUFACTURER'));

			// Set the manufacturer ID
			$this->manufacturer_id = $this->_vm_manufacturer->manufacturer_id;
		}
		else {
			$csvilog->AddStats('incorrect', JText::sprintf('COM_CSVIVIRTUEMART_MANUFACTURER_NOT_ADDED', $this->_vm_product->getError()));
			return false;
		}

		// Store the debug message
		$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_MANUFACTURER_QUERY'), true);
	}

	/**
	 * Adds a reference between manufacturer and product
	 *
	 * @copyright
	 * @author		RolandD
	 * @todo
	 * @see
	 * @access 		private
	 * @param
	 * @return
	 * @since 		3.0
	 */
	private function _manufacturerCrossReference() {
		$csvilog = JRequest::getVar('csvilog');
		$this->_vm_product_mf_xref->bind($this);
		$this->_vm_product_mf_xref->store();
		$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_PROCESS_MANUFACTURER_PRODUCT'), true);
	}

	/**
	 * Creates either an update or insert SQL query for a product price.
	 *
	 * @copyright
	 * @author		RolandD
	 * @todo 		add price calculations
	 * @see
	 * @access 		private
	 * @param
	 * @return
	 * @since 		3.0
	 */
	private function _priceQuery() {
		$csvilog = JRequest::getVar('csvilog');

		// Check if we have a child product with an empty price (will use parents price)
		if ($this->child_product && ($this->product_price == 0 && $this->price_with_tax == 0)) {
			$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_CHILD_NO_PRICE'));
		}
		else {
			// Check if the price is including or excluding tax
			if ($this->product_tax && $this->price_with_tax && is_null($this->product_price)) {
				if (strlen($this->price_with_tax) == 0) $this->product_price = null;
				else $this->product_price = $this->price_with_tax / (1+($this->product_tax/100));
			}
			else if (strlen($this->product_price) == 0) $this->product_price = null;

			// Bind the data
			$this->_vm_product_price->bind($this);

			// Set the default shopper_group_id
			if (is_null($this->shopper_group_id)) {
				// Check if this price has a shopper group ID
				$shopper_group_ids = $this->_vm_product_price->getShopperGroupID();
				$cnt_shopper_group_ids = count($shopper_group_ids);
				if ($cnt_shopper_group_ids <> 1) {
					$default_shopper_group_id = $this->getDefaultShopperGroupID();
					$csvilog->addDebug(JText::sprintf('COM_CSVIVIRTUEMART_DEBUG_FOUND_DEFAULT_SHOPPER_GROUP', $default_shopper_group_id));
					if ($cnt_shopper_group_ids == 0) $this->_vm_product_price->shopper_group_id = $default_shopper_group_id;
					else {
						$shopper_group_key = array_search($default_shopper_group_id, $shopper_group_ids);
						if ($shopper_group_key >= 0) {
							$this->_vm_product_price->shopper_group_id = $shopper_group_ids[$shopper_group_key];
						}
						else {
							$csvilog->AddStats('skipped', JText::_('COM_CSVIVIRTUEMART_PRICE_QUERY_NO_SHOPPER_GROUP'));
							$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_PRICE_QUERY_NO_SHOPPER_GROUP'), true);
							return false;
						}
					}
				}
				else $this->_vm_product_price->shopper_group_id = $shopper_group_ids[0];
			}

			// Calculate the new price
			$this->_vm_product_price->CalculatePrice();

			if (is_null($this->product_price)) {
				// Delete the price
				$this->_vm_product_price->delete();
			}
			else {
				// Store the price
				// Add some variables if needed
				// Set the modified date if the user has not done so
				if (!$this->_vm_product_price->getValue('mdate')) $this->_vm_product_price->setValue('mdate', time());

				// Check if the price already exists
				$this->_vm_product_price->check();

				// Set the create date if the user has not done so and there is no product_price_id
				if (!$this->_vm_product_price->getValue('mdate') && !$this->_vm_product_price->getValue('product_price_id')) {
					$this->_vm_product_price->setValue('cdate', time());
				}

				// Store the price
				$this->_vm_product_price->store();
			}
			$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_PRICE_QUERY'), true);
		}
	}

	/**
	 * Stores the discount for a product
	 *
	 * @copyright
	 * @author		RolandD
	 * @todo 		Add logging
	 * @see
	 * @access 		private
	 * @param
	 * @return
	 * @since 		3.0
	 */
	private function _processDiscount() {
		$csvilog = JRequest::getVar('csvilog');
		$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_PROCESSING_DISCOUNT'));
		if (!is_null($this->amount) && $this->amount > 0) {
			// Bind the data
			$this->_vm_product_discount->bind($this);

			// Add the discount fields
			$this->_vm_product_discount->setValue('start_date', $this->product_discount_date_start);
			$this->_vm_product_discount->setValue('end_date', $this->product_discount_date_end);

			// Check if a discount already exists
			$this->_vm_product_discount->check();
			if (!$this->_vm_product_discount->store()) {
				$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_ADD_DISCOUNT'), true);
				return false;
			}
			// Fill the product information with the discount ID
			$this->product_discount_id = $this->_vm_product_discount->getValue('discount_id');
		}
		else if ($this->amount == 0) {
			$this->product_discount_id = 0;
		}
		else $csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_NO_DISCOUNT'));
	}

	/**
	 * Process attributes for parent product
	 *
	 * @copyright
	 * @author		RolandD
	 * @todo
	 * @see
	 * @access 		private
	 * @param
	 * @return
	 * @since 		3.0
	 */
	private function _processAttributes() {
		// Check if the attributes is to be added
		if ($this->attributes) {
			$csvilog = JRequest::getVar('csvilog');
			$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_ADDING_ATTRIBUTES'));
			$attributes = explode( "|", $this->attributes );
			while(list(,$val) = each($attributes)) {
				$values = explode( "::", $val );
				if (count($values) == 2) {
					// Fix the array to show the correct names to bind the data
					$this->_vm_product_attribute_sku->bind(array('product_id' => $this->product_id, 'attribute_name' => $values[0], 'attribute_list' => $values[1]));
					// Store the data
					$this->_vm_product_attribute_sku->store();
					$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_STORE_ATTRIBUTE'), true);
					// Clean for new insert
					$this->_vm_product_attribute_sku->reset();
				}
				else {
					$csvilog->addDebug(JText::sprintf('COM_CSVIVIRTUEMART_DEBUG_ATTRIBUTE_INCORRECT', $val));
				}
			}
		}
	}

	/**
	 * Process attribute values for child product
	 *
	 * @copyright
	 * @author 		RolandD
	 * @todo
	 * @see
	 * @access 		private
	 * @param
	 * @return
	 * @since 		3.0
	 */
	private function _processAttributeValues() {
		// Check if the attributes is to be added
		if ($this->attribute_values) {
			$csvilog = JRequest::getVar('csvilog');
			$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_ADD_ATTRIBUTE_VALUES'));
			$attribute_values = explode( "|", $this->attribute_values );
			while(list(,$val) = each($attribute_values)) {
				$values = explode( "::", $val );
				if (count($values) == 2) {
					// Fix the array to show the correct names to bind the data
					$this->_vm_product_attribute->bind(array('product_id' => $this->product_id, 'attribute_name' => $values[0], 'attribute_value' => $values[1]));
					// Store the data
					$this->_vm_product_attribute->store();
					$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_ADD_ATTRIBUTE_VALUES'), true);
					// Clean for new insert
					$this->_vm_product_attribute->reset();
				}
				else {
					$csvilog->addDebug(JText::sprintf('COM_CSVIVIRTUEMART_DEBUG_NO_VALID_ATTRIBUTE', $val));
				}
			}
		}
	}

	/**
  	 * Gets the default manufacturer ID
  	 * As there is no default manufacturer, we take the first one
  	 *
  	 * @copyright
  	 * @author		RolandD
  	 * @todo
  	 * @see
  	 * @access		private
  	 * @param
  	 * @return 		integer	database ID of the default manufacturer
  	 * @since
  	 */
	private function _getDefaultManufacturerID() {
		$db = JFactory::getDBO();
		$csvilog = JRequest::getVar('csvilog');

		// Check if product already has a manufacturer link
		if (isset($this->product_sku)) {
			$q = "SELECT manufacturer_id
				FROM #__vm_product_mf_xref m
				LEFT JOIN #__vm_product p
				ON m.product_id = p.product_id
				WHERE product_sku = ".$db->Quote($this->product_sku);
			$db->setQuery($q);
			$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_GET_MANUFACTURER_ID_SKU'), true);
			$mf_id = $db->loadResult();
		}
		else if (isset($this->product_id)) {
			$q = "SELECT manufacturer_id
				FROM #__vm_product_mf_xref
				WHERE product_id = ".$db->Quote($this->product_id);
			$db->setQuery($q);
			$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_GET_MANUFACTURER_ID_ID'), true);
			$mf_id = $db->loadResult();
		}

		// Check if we have a result
		if (!$mf_id) {
			$q = "SELECT manufacturer_id FROM #__vm_manufacturer LIMIT 1 ";
			$db->setQuery($q);
			$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_DEBUG_GET_DEFAULT_MANUFACTURER_ID'), true);
			$mf_id = $db->loadResult();
		}

		$this->manufacturer_id = $mf_id;
	}

	/**
	* Create image name
	*
	* Check if the user wants to have CSVI VirtueMart create the image names if so
	* create the image names without path
	*
	* @copyright
	* @author		RolandD
	* @todo
	* @see 			processImage()
	* @access 		private
	* @param
	* @return
	* @since 		3.0
	*/
	private function _createImageName() {
		$csvilog = JRequest::getVar('csvilog');
		$template = JRequest::getVar('template');

		$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_GENERATE_IMAGE_NAME'));

		// Create extension
		$ext = $template->getValue('auto_generate_image_name_ext', 'image');

		// Check if the user wants to convert the images to a different type
		switch ($template->getValue('type_generate_image_name', 'image')) {
			case 'product_sku':
				$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_CREATE_PRODUCT_SKU_NAME'));
				if (!is_null($this->product_sku)) $name = $this->product_sku;
				else {
					$csvilog->AddStats('error', JText::_('COM_CSVIVIRTUEMART_CANNOT_FIND_PRODUCT_SKU'));
					return false;
				}
				break;
			case 'product_name':
				$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_CREATE_PRODUCT_NAME_NAME'));
				if (!is_null($this->product_name)) $name = $this->product_name;
				else {
					$csvilog->AddStats('error', JText::_('COM_CSVIVIRTUEMART_CANNOT_FIND_PRODUCT_NAME'));
					return false;
				}
				break;
			case 'product_id':
				$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_CREATE_PRODUCT_ID_NAME'));
				if (!is_null($this->product_id)) $name = $this->product_id;
				else {
					$csvilog->AddStats('error', JText::_('COM_CSVIVIRTUEMART_CANNOT_FIND_PRODUCT_ID'));
					return false;
				}
				break;
			case 'random':
				$csvilog->addDebug(JText::_('COM_CSVIVIRTUEMART_CREATE_RANDOM_NAME'));
				$name = mt_rand();
				break;
		}

		$image_name = $name.'.'.$ext;
		$csvilog->addDebug(JText::sprintf('COM_CSVIVIRTUEMART_CREATED_IMAGE_NAME', $image_name));
		$this->product_full_image_output = $image_name;
		$this->product_thumb_image_output = 'resized/'.$image_name;

		// Check if the user is supplying image data
		if (is_null($this->product_full_image)) $this->product_full_image = $this->product_full_image_output;
		if (is_null($this->product_thumb_image)) $this->product_thumb_image = $this->product_thumb_image_output;
		return true;
	}
}
?>