<?php

/* * *************************************************************
 *  Copyright notice
 *
 *  (c) 2007 Thomas Waggershauser <waggershauser@airware.de>
 *  (c) 2018 Stefan Beyer SEDAT GmbH <stefan@sedat.de>
 *  All rights reserved
 *
 *  This script is part of Cy4Marktzeitung. The Cy4Marktzeitung project 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.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *
 *  This script 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.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 * ************************************************************* */
if (!defined('ENTRY_POINT'))
  die('Falscher Aufruf');


require_once 'RestClient.php';

class AdsManager {

  // $cyclos-version
  var $cyclos_version;
  // cyclos url
  var $cyclos_root;
  // The SOAP object ( nusoap or phpsoap )
  var $restClient;

  var $categories;
  var $categoryTree;
  var $page;
  var $ads = [];
  var $orderBy;

  function __construct() {
    $this->cyclos_root = CYCLOS_API_URL;

    $this->restClient = new RestClient($this->cyclos_root);
    $this->restClient->setCredetials(CYCLOS_API_USER, CYCLOS_API_PASSWORD);

    // Am ende ist oderBy[0] das feld für die api und orderBy[1]==true gibt an, ab die liste umgedreht werden soll
    if (defined('AD_SEARCH_ORDER_BY') && AD_SEARCH_ORDER_BY) {
      $orderBy = explode(' ', AD_SEARCH_ORDER_BY);
      if (count($orderBy) === 1) {
        $orderBy[] = 'desc';
      }
      if ($orderBy[0] === 'price') {
        /// Sonderbehaldlung für priceAsc, priceDesc
        if ($orderBy[1]==='desc') {
          $orderBy[0] = $orderBy[0] . 'Desc';
        } else {
          $orderBy[0] = $orderBy[0] . 'Asc';
        }
        $orderBy[1] = false;
      } else if ($orderBy[0] === 'random') {
        $orderBy[1] = false;
      }
      // date default: absteigend desc
      if ($orderBy[1] !== false) {
        $orderBy[1] = $orderBy[1] === 'asc'; // nur wenn asc, muss umgedreht werden
      }
      $this->orderBy = $orderBy;
    } else {
      $this->orderBy = null;
    }

    // Version erfragen
    $response = $this->restClient->get('/ui/data-for-ui', ['fields' => 'cyclosVersion']);
    $data = $response->getBodyJSON();
    if ($response->isOK()) {
      console('/ui/data-for-ui');
      console($data);
      $this->cyclos_version = array_map('intval', explode('.', $data->cyclosVersion));
    } else {
      console('/ui/data-for-ui: '.$response->getStatus());
      console($data);
    }
    
  }

  /**
   * Search for ads
   */
  function search($params = array()) {
    $ads = [];
    $params['page'] = 0;
    $params['statuses'] = 'active';

    if (!empty($this->orderBy) && !empty($this->orderBy[0])) {
      $params['orderBy'] = $this->orderBy[0];
    }
    do {
      $result = $this->restClient->get('/marketplace', $params);
      if (!$result->isOK()) {
        console('REST Fehler:');
        o($result->getBodyJSON());
        die('Fehler in search');
      }
      $data = $result->getBodyJSON();
      $ads = array_merge($ads, $data);
      $params['page'] ++;
    } while ($result->isOK() && $result->getHeader('x-has-next-page') == 'true');
    //console($ads);
    if ($this->orderBy[1] ?? false) {
      $ads = array_reverse($ads);
    }
    return $ads;
  }

  // Load user - cached for 1 request
  function &loadUser($id) {
    static $cache = [];
    if (!isset($cache[$id])) {
      $cache[$id] = null;
      $result = $this->restClient->get('/users/' . $id);
      if ($result->isOK()) {
        $cache[$id] = $result->getBodyJSON();
      } else {
        console('REST Fehler loadUser('.$id.'): '.$result->getBodyRaw());
        return null;
        //o($result->getBodyJSON());
        //die('in loadUser('.$id.')');
      }
    }
    return $cache[$id];
  }

  /**
   * load single ad details
   *
   * @param int $id
   */
  function loadDetails($id) {
    $result = $this->restClient->get('/marketplace/' . $id);
    if (!$result->isOK()) {
      console('REST Fehler loadDetails():');
      echo 'API call failed for /marketplace/<id> with Code '.$result->getStatus();
      o($result->getBodyJSON());
      die('Fehler in loadDetails');
    }
    $data = $result->getBodyJSON();
    return $data;
  }

  /**
   * list categories
   *
   * @return array of categories
   */
  function listCategories() {
    global $local;

    //console('listCategories');

    $response = $this->restClient->get('/marketplace/data-for-search', []);
    if (!$response->isOK()) {
      $this->categories = null;
      console('Failed');
      console($response->getStatus());
      echo 'API call failed for /marketplace/data-for-search with Code '.$response->getStatus();
      o($response->getBodyJSON());
      die('in listCategories()');
      return null;
    }
    $data = $response->getBodyJSON();

    //console('OK');

    $categories = $data->categories;

    $this->categories = [];

    $def = new stdClass;
    $def->id = '0';
    $def->name = "NOCATEGORY";
    $def->children = [];
    array_unshift($categories, $def);
    unset($def);

    $this->categories = $this->_categories($categories);

    //console($this->categories);

    return $this->categories;
  }

  // hilfsfunktion zum einlesen der cats
  private function _categories(&$categories) {
    $result = [];
    foreach ($categories as &$c) {
      $c->children = $this->_categories($c->children);
      $result['' . $c->id] = $c;
    }
    return $result;
  }
  
  
  
  /**
   * Verwalten des caches für die liste der kategorien
   * laden & speichern!
   *
   */
  function cacheCategories() {
    #echo 'cacheCategories', PHP_EOL;
    $categoryFile = PDF_CACHE_PATH . 'categories.tmp';

    // Alter überprüfen
    if (( @filectime($categoryFile) + PDF_CACHE_TIME ) < time()) {
      // ggf löschen
      @unlink($categoryFile);
    }

    if (is_file($categoryFile) && PDF_CACHE) {
      #	// laden
      $f = fopen($categoryFile, 'r');
      $cf = fread($f, filesize($categoryFile));
      $this->categories = unserialize($cf);
      //console('categories from cache');
    } else {
      // über soap hohlen
      $this->listCategories();

      // speichern
      if (PDF_CACHE) {
        $f = fopen($categoryFile, 'w');
        fwrite($f, serialize($this->categories));
        fclose($f);
      }
    }

    //Text fuer "Alle Kategorien" vergeben
    if (is_array($this->categories)) {
      if ($this->categories['0']->id == '0') {
        $this->categories['0']->name = PDF_ALLCATEGORIES_TEXT;
      }
    }
  }

  /**
   * Verwalten des Caches für eine einzelne Kategorie des Katalogs
   *
   * @param unknown_type $category
   * @return bool Muss Kategorie noch ermittelt werden oder nicht..
   */
  function loadCatalogCache($id, &$catalog) {
    //$catfile = PDF_CACHE_PATH . $category->name . '.tmp';
    $catfile = $this->genCatalogFileName($id);
    if (is_file($catfile) && PDF_CACHE) {
      // Alter ermitteln und prüfen ob noch gültig
      // falls nicht löschen und false zurückgeben
      if (( filectime($catfile) + PDF_CACHE_TIME ) < time()) {
        unlink($catfile);
        return false;
      }

      // file laden
      $f = fopen($catfile, 'r');
      $cf = fread($f, filesize($catfile));
      $ca = unserialize($cf);
      if (is_array($ca)) {
        $catalog[$id] = $ca;
      }
      fclose($f);

      return true;
    } else {
      return false;
    }
  }

  /**
   * Aus hierarchischer liste wird flache liste:
   * [id1] => [name1Level0, name1Level1, ...]
   * [id2] => [name2Level0, name2Level1, ...]
   */
  function getFlatCategories() {
    return $this->_flatcathelper($this->categories);
  }

  private function _flatcathelper(&$c) {
    $result = [];
    foreach ($c as &$_c) {
      if (count($_c->children) === 0) {
        $result[$_c->id] = [$_c->name];
      } else {
        $res = $this->_flatcathelper($_c->children);
        foreach ($res as $k => &$r) {
          if (!isset($result[$k])) {
            $result[$k] = [$_c->name];
          }
          $result[$k] = array_merge($result[$k], $r);
        }
      }
    }
    return $result;
  }

  /**
   * Das Array erzeugen, das den Inhalt für unser PDF enthält..
   *
   * @param $tradetype array('OFFER')  ........... only offer-ads
   *                   array('SEARCH') ........... only search-ads
   *                   array('OFFER','SEARCH') ... both (default)
   */
  function getCatalog($tradeTypes) {
    
    // TODO By so complicated - einfach array mit ads zurück geben?

    #nuetzt nix:
    #set_time_limit(300);
    #ini_set('memory_limit', '500M');

    //unset($this->catalog);
    $catalog = [];
    // Falls wir keine Kategorien haben, die aus dem soap-object verwenden
    if (is_null($this->categories)) {
      $this->cacheCategories();
    }

    if (!is_array($this->categories)) {
      return null;
    }

    $cats = $this->getFlatCategories();

    // zu debugzwecken kürzen...
    //if (defined('DEBUG_MAX_CATEGORIES')) {
    //  $cats = array_slice($cats, 0, DEBUG_MAX_CATEGORIES);
    //}
    #o($cats);
    //wenn die Kategorien einzeln angezeigt werden (sortiert nach Kategorien)
    $catCount = 0;
    
    foreach ($cats as $id => $names) {
      if (PDF_ALLCATEGORIES == false) {
        if ($id == "0")
          continue;
      }
      //o($c);
      // Aufgrund der größe, etc das ganze aufteilbar machen..
      if (!$this->loadCatalogCache($id, $catalog)) {
        // verfügbare trade types
        #$tradeTypes = Array( 'OFFER', 'SEARCH' );

        foreach ($tradeTypes as $type) {
          $temp = $this->buildTradeType($id, $names, $type);
          #print_ra($temp,"temp-ads");
          //leere ads nicht eintragen
          //if( !is_array($temp) || !empty($temp)) continue;
          //echo , $type;
          //o([$names, $type, $temp]);

          $catalog[$id][$type] = $temp;
        }
        #	o($this->catalog);
        // speichern
        if (PDF_CACHE) {
          //$f = fopen( PDF_CACHE_PATH . $c->name . '.tmp', 'w' );
          $name = $this->genCatalogFileName($id);
          //$f = fopen( $name, 'w' );
          if (!$f = fopen($name, 'w')) {
            //print_r($c);
          }
          fwrite($f, serialize($catalog[$id]));
          fclose($f);
        }
      } else {
        //console('Cat ' . $id . ' from cache.');
      }
      $catCount++;

      if (defined('DEBUG_MAX_CATEGORIES') && $catCount >= DEBUG_MAX_CATEGORIES) {
        break;
      }
    } // foreach categorie
    return $catalog;
  }

  /**
   * Generate filename for catalog-cache
   *
   */
  function genCatalogFileName($cat_id) {
    //$name = PDF_CACHE_PATH . utf8_decode( $cat->name ) . '.tmp';
    $name = PDF_CACHE_PATH . 'cat' . base64_encode($cat_id) . '.tmp';
    return $name;
  }

  /**
   * TradeType in Catalog
   *
   * @param object / array $c
   * @param string $tradeType
   */
  function buildTradeType($cat_id, $cat_names, $tradeType) {
    # DEBUG
    /* if ($tradeType == 'OFFER') {
      return [json_decode('{
      "id": 1,
      "owner": {"username": "s.beyer", "display": "Stefan Beyer", "email": "ich@wir.com"},
      "name": "Test Eins",
      "description": "Die Erde unter den nackten Füßen spüren, den Regen auf der Haut und den Wind im Gesicht. Von dem leben, was wild entsteht und was durch die eigene Arbeit möglich wird. Ein Leben, das so weit wie möglich selbstbestimmt und frei ist. In der Selbstversorgung und kreativer Arbeit hab ich genau das gefunden. Die Erde unter den nackten Füßen spüren, den Regen auf der Haut und den Wind im Gesicht. Von dem leben, was wild entsteht und was durch die eigene Arbeit möglich wird. Ein Leben, das so weit wie möglich selbstbestimmt und frei ist. In der Selbstversorgung und kreativer Arbeit hab ich genau das gefunden. Die Erde unter den nackten Füßen spüren, den Regen auf der Haut und den Wind im Gesicht. Von dem leben, was wild entsteht und was durch die eigene Arbeit möglich wird. Ein Leben, das so weit wie möglich selbstbestimmt und frei ist. In der Selbstversorgung und kreativer Arbeit hab ich genau das gefunden. Die Erde unter den nackten Füßen spüren, den Regen auf der Haut und den Wind im Gesicht. Von dem leben, was wild entsteht und was durch die eigene Arbeit möglich wird. Ein Leben, das so weit wie möglich selbstbestimmt und frei ist. In der Selbstversorgung und kreativer Arbeit hab ich genau das gefunden. Die Erde unter den nackten Füßen spüren, den Regen auf der Haut und den Wind im Gesicht. Von dem leben, was wild entsteht und was durch die eigene Arbeit möglich wird. Ein Leben, das so weit wie möglich selbstbestimmt und frei ist. In der Selbstversorgung und kreativer Arbeit hab ich genau das gefunden.",
      "price": 4.99,
      "currency": {"decimalDigits": 2, "suffix": "€"},
      "publicationPeriod": {
      "begin": "2019-01-17T00:00:00.000+01:00",
      "end": "2019-04-17T23:59:59.999+02:00"
      }
      }')];
      //return json_decode(file_get_contents('stuff/offers.json'));
      } else {
      return [json_decode('{
      "id": 1,
      "owner": {"username": "s.beyer", "display": "Stefan Beyer", "email": "ich@wir.com"},
      "name": "Test Zwei",
      "description": "Mir ist die Welt ein bisschen zu abstrakt geworden. Ja, das sage ich als Programmierer. Ich habe das starke Bedürfnis in vielen Dingen mehr mit dem zu tun zu haben, was ich benutze, was ich brauche. Ich will wissen, wo die Dinge her kommen, ich will spüren, was sie Wert sind. Das sind meine Aufgaben: Programmierung, Grafikdesign, Handwerkliches Mein Traum: Schäfer einer kleinen Schafherde sein, eine eigene kleine Outdoor-Schmiede haben, pedalbetriebene Werkzeuge bauen und benutzen. Mir ist die Welt ein bisschen zu abstrakt geworden. Ja, das sage ich als Programmierer. Ich habe das starke Bedürfnis in vielen Dingen mehr mit dem zu tun zu haben, was ich benutze, was ich brauche. Ich will wissen, wo die Dinge her kommen, ich will spüren, was sie Wert sind. Das sind meine Aufgaben: Programmierung, Grafikdesign, Handwerkliches Mein Traum: Schäfer einer kleinen Schafherde sein, eine eigene kleine Outdoor-Schmiede haben, pedalbetriebene Werkzeuge bauen und benutzen.",
      "price": 4.99,
      "currency": {"decimalDigits": 2, "suffix": "€"},
      "publicationPeriod": {
      "begin": "2019-01-17T00:00:00.000+01:00",
      "end": "2019-04-17T23:59:59.999+02:00"
      }
      }')];
      //return [];
      } */


    $params = Array(
        "fields" => 'id,name',
        "category" => $cat_id,
    );
    $params = array_merge($params, AD_FILTER);
    $this->createTradeTypeFilter($params, $tradeType);
    $ads = $this->search($params);
    #o($params);
    #o($ads);
    // ersetzen durch vollständigen datensatz
    foreach ($ads as &$ad) {
      $ad = $this->loadDetails($ad->id);
      $ad->owner = $this->loadUser(($ad->owner ?? $ad->user)->id);
      if (defined('DEBUG_DESCRIPTION')) {
        if (DEBUG_DESCRIPTION === true && function_exists('debug_description')) {
          $ad->description = debug_description();
        } else if (DEBUG_DESCRIPTION) {
          $ad->description = DEBUG_DESCRIPTION;
        }
      }
    }
    //console($ads, 5);
    return $ads;
    //return $this->getAds();
  }

  function createTradeTypeFilter(&$params, $tradeType) {
    $field = TRADE_TYPE_CUSTOM_FIELD;
    $value = constant('TRADE_TYPE_' . $tradeType . '_INTERNAL_VALUE');
    $params['customFields'] = $field . ':' . $value;
  }
  

}
