<?php
/**
 * @package    pixelcms_articles
 *
 * @author     christophf <your@email.com>
 * @copyright  A copyright
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 * @link       http://your.url.com
 */

use Joomla\Registry\Registry;

defined('_JEXEC') or die;

require_once 'BaseModel.php';

class PropertyObject {
    public $name;
    public $show;
    public $value;
    public $type = 'bool';

    public function __construct(string $name, $value, string $type = 'bool')
    {
        $this->name = $name;
        $this->value = $value;
        $this->type = $type;

        $this->show = (string) $value;
    }

    /**
     * @param ['name', 'value'] $element
     *
     * @return static
     *
     * @since version
     */
    public static function fromArray($element): self
    {
        return new self((string) $element['name'], (string) $element['value'], 'enum');
    }

    public static function fromXML(SimpleXMLElement $element): self
    {
        return new self((string) $element['name'], (string) $element['name']);
    }

    public function __toString(): string
    {
        return $this->show;
    }
}

class LandwirtProps {
    /**
     * @var SimpleXMLElement
     */
    static $_definitions;
    /**
     * @var SimpleXMLElement
     */
    private $definitions;

    static function loadDefinitions(): void {
        $feed = file_get_contents(JPATH_BASE . '/maschinendaten/definitions.xml');
        $feed = preg_replace("/<([^>\s]*)\s*(?:xmlns=*[\"'][^\"']*[\"'])[^>]*>/i", "<$1>", $feed);
        $feed = preg_replace('/&(?!#?[a-z0-9]+;)/', '&amp;', $feed);
        static::$_definitions = simplexml_load_string($feed, null, LIBXML_NOCDATA);
    }

    /**
     * @param int|string $prop
     *
     * @return string|null
     *
     */
    static function getMapping($prop): ?string {
        $mapping = array_flip(static::getDefinitions());

        return $mapping[$prop] ?? null;
    }

    /**
     * @param int|string $prop
     *
     * @return int|string|null
     */
    static function getConstByProp($prop) {
        return static::getDefinitions()[static::getMapping($prop)] ?? null;
    }

    protected $shown = [];
    protected $product;

    public function __construct(SimpleXMLElement $product)
    {
        $this->product = $product;

        $catId = (int) $product->subcat['id'];

        $this->definitions = static::$_definitions->xpath("./subcat[@id='{$catId}']")[0];
    }

    public function getSelectionProp(string $prop): ?SimpleXMLElement
    {
        $definition = $this->definitions->xpath("./selection_property/property[@short='{$prop}']/..");

        return ($definition) ? $definition[0] : null;
    }

    public function getProp(SimpleXMLElement $prop)
    {
        $name = (string) $prop['short'];
        if (isset($this->shown[$name])) return null;

        $this->shown[$name] = true;

        if ($definition = $this->getSelectionProp($name)) {
            return PropertyObject::fromArray([
                'name' => $definition['name'],
                'value' =>  $prop['name'],
            ]);
        }

        return PropertyObject::fromXML($prop);
    }

    public function getAllProps(): array
    {
        return array_map(function(SimpleXMLElement $prop) {
            return $this->getProp($prop);
        }, $this->product->xpath('./property'));
    }
}

class customField {
    private SimpleXMLElement $product;

    public function __construct(SimpleXMLElement $product)
    {
        $this->product = $product;
    }

    public function getProp(SimpleXMLElement $prop)
    {
        return PropertyObject::fromArray([
            'name' => $prop['name'],
            'value' =>  $prop['value'],
        ]);
    }

    public function getAllCustoms(): array
    {
        return array_map(function(SimpleXMLElement $prop) {
            return $this->getProp($prop);
        }, $this->product->xpath('./custom_field'));
    }
}

class Category {
    public $id;
    public $name;

    public function  __construct(SimpleXMLElement $product) {
        $this->id = (int) $product->cat['id'];
        $this->name = (string) $product->cat['value'];
    }
}

class Condition {
    public $id = 'new';

    const MAPPING = [
        'used',
        'demonstration',
        'new',
        'rental',
    ];

    public function  __construct(SimpleXMLElement $product) {
        $this->id = $this->getIdMapping((int) $product->quality);
    }

    private function getIdMapping(?int $condition)
    {
        return self::MAPPING[$condition] ?? $condition;
    }
}

class Vat {
    public $value = null;
    public $separable = true;

    public function  __construct(SimpleXMLElement $product) {
        $valueString = (string) $product->vatoption['value'];

        $matched = preg_match('/in[ck]l\. ([\d,]+)% (?:Vat|MwSt)/i', $valueString, $matches);

        $this->value = $matched ? (floatval(str_replace(',','.',$matches[1])) / 100) : null;
    }
}

class Price {
    public $currency = 'EUR';
    public $specialprice = false;
    public $values;

    public function __construct(SimpleXMLElement $product, Vat $vat)
    {
        $price = (float) $product->price['value'];
        $this->currency = (string) $product->price['currency'];

        // Only products with prices get calculated
        if ($price > 0 && $vat->value) {
            $this->values = (object) [
                'netto' => $price / (1 + $vat->value),
                'brutto' => $price,
            ];
        }
    }
}

class Workhours {
    public $value;
    public $unit = 'Std.';
    public $show;

    public function  __construct(SimpleXMLElement $product) {
        $this->value = (float) $product->hours;
        $this->show = number_format($this->value, 0, '', '.') . " $this->unit";
    }
}

class Power {
    public $PS;
    public $kW;

    public function  __construct(SimpleXMLElement $product) {
        $this->PS = (float) $product->power['value'];
        $this->kW = (float) round($this->PS / 1.36);
    }
}

class Manufacturer {
    public $id;
    public $value;

    public function  __construct(SimpleXMLElement $product) {
        $this->id = (int) $product->brand['id'];
        $this->value = (string) $product->brand['value'];
    }
}

class Product {
    public $id;
    public $dealer_internal_id;
    public $status;
    public $condition;
    public $category;
    public $manufacturer;
    public $model;
    public $price;
    public $vat;
    public $free_text;
    public $equipment = [];
    public $additional = [];

    public function  __construct(SimpleXMLElement $product) {
        $props = new LandwirtProps($product);
        $customs = new customField($product);

        $this->id = (int) $product['id'];
        $this->dealer_internal_id = $this->id;
        $this->status = 1;
        $this->manufacturer = new Manufacturer($product);
        $this->condition = new Condition($product);
        $this->category = new Category($product);
        $this->vat = new Vat($product);
        $this->price = new Price($product, $this->vat);
        $this->model = (string) $product->type;

        $this->free_text = (string) $product->description;

        if (intval($product->hours) > 0) {
            $this->workhours = new Workhours($product);
        }
        if (intval($product->power) > 0) {
            $this->power = new Power($product);
        }

        $this->year = (string) $product->year;

        $allProps = $props->getAllProps();
        $allCustoms = $customs->getAllCustoms();

        /** @var PropertyObject $prop */
        foreach ($allProps as $prop) {
            if ($prop->type === 'enum') {
                $this->additional[] = $prop;
            } else {
                $this->equipment[] = $prop;
            }
        }
        foreach ($allCustoms as $prop) {
            $this->additional[] = $prop;
        }
        $this->equipment = array_values(array_filter($this->equipment));
        $this->additional = array_values(array_filter($this->additional));

        $this->images = array_map(function (SimpleXMLElement $image) {
            $url = (string) $image['name'];
            return (object) [
                'href' => $url,
            ];
        }, $product->xpath('./picture'));

        $this->contact_persons = null;
    }
}

class Pixelcms_articlesModelLandwirt_products  extends BaseModel
{
    protected $module = 'traktorpool';
    protected $api_key;
    protected $articleId;
    protected $customer_id;
    protected $contact_persons;
    protected $password;
    protected $username;
    protected $language;
    protected $url;
    protected $ads;
    protected $allParams;
    protected $menuId;
    protected $filters = [];

    protected $defaults = [
        'print_btn' => 1,
        'price_style' => 0,
        'speed', 750,
        'disable_title_image' => 0,
    ];

    protected $hidden = [
        'tracktorpool_apikey',
        'tracktorpool_customer_id',
        'tracktorpool_username',
        'tracktorpool_password',
        'traktorpool_condition',
        'traktorpool_status',
    ];

    public function __construct(array $config = array())
    {
        $lang = JFactory::getLanguage();
        $langCode = $lang->getTag();
        $langCodeShort = substr($langCode, 0, 2);

        parent::__construct($config);

        /** @var ExtendedRegistry $params - Settings for current Menu Item */
        $params = $this->getState('params');

        $this->menuId = $params->getInt('Itemid');

        $this->customer_id = $params->get('tracktorpool_customer_id');

        //$this->print_btn = $jinput->getInt('print_btn');
        $this->articleId = $params->get('articleId', null);

        $this->language = $langCode;

        $this->ads = self::getAds();
    }

    private function getAds()
    {
        $ads = self::checkCache();

        foreach ($ads as $ad) {
            $query = http_build_query(array_merge($this->params->toArray(), [
                'articleId' => $ad->id,
                'Itemid' => $this->menuId,
            ]));
            $ad->link = JRoute::_("index.php?$query");
        }

        usort($ads, function($b, $a) {
            if ($a->id == $b->id) {
                return 0;
            }
            return ($a->id < $b->id) ? -1 : 1;
        });

        return $ads;
    }

    public function getRelatedAds()
    {
        $ad = $this->getData();
        $relatedAds =  array_values(array_filter($this->ads, function ($relatedAd) use ($ad) {
            return !!($ad->category->id == $relatedAd->category->id && $ad->id != $relatedAd->id);
        }));

        self::fisherYatesShuffle($relatedAds, $ad->id);

        return $relatedAds;
    }

    private static function fisherYatesShuffle(&$items, $seed)
    {
        if (is_string($seed)) {
            mt_srand(crc32($seed));
        } else {
            mt_srand($seed);
        }
        $items = array_values($items);
        for ($i = count($items) - 1; $i > 0; $i--)
        {
            $j = mt_rand(0, $i);
            $tmp = $items[$i];
            $items[$i] = $items[$j];
            $items[$j] = $tmp;
        }
    }

    public function getParams()
    {
        return $this->params;
    }

    /**
     *
     * @return mixed
     *
     * @since version
     * @throws Exception
     */
    private function fetchAds()
    {
        LandwirtProps::loadDefinitions();

        // $xmlBody = $this->cURL("http://services.mascus.com/api/getexport.aspx?exportid=$this->customer_id");
        $feed = file_get_contents(JPATH_BASE . '/maschinendaten/export_g189.xml');
        $feed = preg_replace("/<([^>\s]*)\s*(?:xmlns=*[\"'][^\"']*[\"'])[^>]*>/i", "<$1>", $feed);
        $feed = preg_replace('/&(?!#?[a-z0-9]+;)/', '&amp;', $feed);
        $xml = simplexml_load_string($feed, null, LIBXML_NOCDATA);

        $dealers = $xml->xpath("./firm");
        $products = [];
        foreach($dealers as $dealer) {
            $products[] = array_map(function($product) use ($dealer) {
                return new Product($product);
            }, $dealer->xpath('./offers/offer'));
        }

        return array_reduce($products, 'array_merge', []);
    }

    private function checkCache($force = false)
    {
        $cacheKey = "landwirt_products";
        $ads = $this->cache->get($cacheKey, $this->module);

        if (empty($ads) || $force) {
            $ads = $this->fetchAds();
            $this->cache->store( serialize($ads), $cacheKey, $this->module);
        } else {
            $ads = unserialize($ads);
        }

        return $ads ?? [];
    }

    public function revalidateCache()
    {
        $this->ads = $this->checkCache(true);
    }

    public function getData()
    {
        if ($this->articleId) {
            foreach ($this->ads as $key => $product) {
                if ($product->id == $this->articleId) {
                    return $product;
                }
            }
        }
        return $this->ads;
    }
}
