<?php
/**
 * Backend API Českéreality.cz.
 *
 * @category   Dalten
 * @package	   Export
 * @subpackage Api
 */
class Dalten_Export_Api_Backend_Ceskereality implements Dalten_Export_Api_Backend_LoggableBackendInterface
{
	private $_http = null;
	private $_login = null;
	private $_password = null;
	private $_clientId = null;
	private $_prefix = null;
	private $_swKey = null;
	private $_tempDir = null;
	private $_workDir = null;
	private $_logger = null;
	private $_brokerFaceSent = array();
	private $_exportEndpoint = '';
	private $_exportJmeno = '';
	private $_exportVerze = '1.0';

	/**
	 * Konstruktor. Nastavuje závislosti.
	 *
	 * @param Dalten_Http_ClientInterface $http        HTTP klient.
	 * @param string                      $tmpdir      Cesta k adresáři dočasných souborů JEN pro tento export.
	 * @param string                      $endpoint    Endpoint exportu.
	 * @param string                      $exportJmeno Název exportního software.
	 */
	function __construct(
		Dalten_Http_ClientInterface $http,
		$tmpdir,
		$endpoint='http://export.ceskereality.cz/import_realmanager',
		$exportJmeno='Irest/DALTEN.cz'
	)
	{
		$this->_http = $http;
		$this->_tempDir = $tmpdir;
		$this->_exportEndpoint = $endpoint;
		$this->_exportJmeno = $exportJmeno;
	}

	/**
	 * Vytvoří dočasný pracovní adresář.
	 */
	function createWorkDir()
	{
		do {
			$workDir = $this->_tempDir . DIRECTORY_SEPARATOR . 'ceskereality' . mt_rand(0, 9999999);
		} while (!mkdir($workDir));
		$this->_workDir = $workDir;
	}

	/**
	 * Smaže dočasný pracovní adresář.
	 */
	function removeWorkDir()
	{
		$dirPath = $this->_workDir;
		foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dirPath, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST) as $path) {
			$path->isDir() ? rmdir($path->getPathname()) : unlink($path->getPathname());
		}
		rmdir($dirPath);
	}

	/**
	 * Zkontroluje login a uloží si přihlašovací údaje.
	 *
	 * @param string $login            Přihlašovací jméno.
	 * @param string $password         Heslo.
	 * @param string $softwareKey      Softwareový klíč.
	 * @param array  $additionalParams Další parametry (client_id, prefix).
	 *
	 * @return Dalten_Export_Api_ServerResponse Vrátí zda se podařilo přihlásit.
	 */
	function checkLogin($login, $password, $softwareKey='', array $additionalParams = array() )
	{
		if (empty($login) && empty($password)) {
			// pokud nebylo zadáno jméno a heslo, České reality vracely OK
			return new Dalten_Export_Api_ServerResponse(false, 500, 'Nevyplneno jmeno!');
		}
		$this->_login = $login;
		$this->_password = $password;
		$response = $this->_http->get(
			sprintf(
				$this->_exportEndpoint . '/autorizace.html?jmeno=%s&heslo=%s',
				urlencode($login),
				urlencode(md5($password))
			)
		);
		if ($this->_jeOK($response)) {
			$responseLines = explode("\n", $response);
			$this->_prefix = $responseLines[1];
			$this->_clientId = $responseLines[2];
		}
		return $this->_handleResponse($response);
	}

	/**
	 * Pošle nabídku na server.
	 *
	 * @param int   $idNabidka        ID nabídky.
	 * @param array $listingData      Převedené informace o nabídce.
	 * @param array $userData         Převedené informace o uživatelích.
	 * @param array $images           Pole obrázků.
	 * @param array $additionalParams Další parametry (nepoužívá se).
	 *
	 * @return Dalten_Export_Api_ServerResponse Vrátí zda se podařilo poslat, nebo na čem to vybuchlo.
	 */
	function addAdvert(
		$idNabidka,
		array $listingData,
		array $userData,
		array $images = array(),
		array $additionalParams = array()
	)
	{
		$this->createWorkDir();

		$this->_uploadBrokerFace($userData);

		unset($userData['foto']);

		$images = $this->_uploadImages($idNabidka, $images);

		$nemovitostiPath = $this->_workDir . DIRECTORY_SEPARATOR . 'nemovitosti.xml';
		$xml =  $this->_generateXML($idNabidka, 'U', $listingData + $userData, $images);

		if ($this->_logger) {
			$this->_logger->logRemoteCall('addAdvert', $xml);
		}

		file_put_contents($nemovitostiPath, $xml);

		$response = $this->_http->post(
			$this->_exportEndpoint . '/uloz_xml.html',
			array(
				'jmeno' => $this->_login,
				'heslo' => md5($this->_password),
				'id' => $this->_clientId,
				'prefix' => $this->_prefix
			),
			array(
				's' => $nemovitostiPath
			)
		);

		$this->removeWorkDir();

		if ($this->_jeOK($response)) {
			$finalResponse = $this->_http->get(
				sprintf(
					$this->_exportEndpoint . '/import_xml.html?jmeno=%s&heslo=%s&id=%d&prefix=%s',
					urlencode($this->_login),
					urlencode(md5($this->_password)),
					urlencode($this->_clientId),
					urlencode($this->_prefix)
				)
			);

			if ($this->_jeOK($finalResponse)) {
				$urlResponse = $this->_http->get(
					sprintf(
						$this->_exportEndpoint . '/url_nemovitosti.html?jmeno=%s&heslo=%s&id=%d&prefix=%s&id_nemovitosti=%d',
						urlencode($this->_login),
						urlencode(md5($this->_password)),
						urlencode($this->_clientId),
						urlencode($this->_prefix),
						urlencode($idNabidka)
					)
				);
				if ($this->_jeOK($urlResponse)) {
					$urlLines = explode("\n", $urlResponse);
					$url = $urlLines[1];
					$return = new Dalten_Export_Api_ServerResponse(true);
					$return->setData(
						array(
							'remoteUrl' => $url
						)
					);
					return $return;
				}
			}
			return $this->_handleResponse($finalResponse);
		} else {
			return $this->_handleResponse($response);
		}
	}

	/**
	 * Stáhne nabídku ze serveru.
	 *
	 * @param int   $idNabidka   ID nabídky.
	 * @param array $listingData Data o nabídce.
	 *
	 * @return Dalten_Export_Api_ServerResponse Vrátí zda se podařilo smazat, nebo na čem to vybuchlo.
	 */
	function delAdvert($idNabidka, $listingData)
	{
		$this->createWorkDir();

		$nemovitostiPath = $this->_workDir . DIRECTORY_SEPARATOR . 'nemovitosti.xml';

		$xml =  $this->_generateXML($idNabidka, 'D', $listingData, array());

		if ($this->_logger) {
			$this->_logger->logRemoteCall('delAdvert', $xml);
		}

		file_put_contents($nemovitostiPath, $xml);

		$response = $this->_http->post(
			$this->_exportEndpoint . '/uloz_xml.html',
			array(
				'jmeno' => $this->_login,
				'heslo' => md5($this->_password),
				'id' => $this->_clientId,
				'prefix' => $this->_prefix
			),
			array(
				's' => $nemovitostiPath
			)
		);

		$this->removeWorkDir();

		if ($this->_jeOK($response)) {
			$finalResponse = $this->_http->get(
				sprintf(
					$this->_exportEndpoint . '/import_xml.html?jmeno=%s&heslo=%s&id=%d&prefix=%s',
					urlencode($this->_login),
					urlencode(md5($this->_password)),
					urlencode($this->_clientId),
					urlencode($this->_prefix)
				)
			);

			return $this->_handleResponse($finalResponse);
		} else {
			return $this->_handleResponse($response);
		}
	}

	/**
	 * Nahraje obrázky nemovitosti a vrátí upravené informace o obrázcích.
	 *
	 * @param int   $idNabidka ID nabídky ke které obrázky patří.
	 * @param array $images    Pole informací s obrázky.
	 *
	 * @return array Pole s obrásky s přidanou md5.
	 */
	protected function _uploadImages($idNabidka, array $images)
	{
		$imageListRaw = $this->_http->get(
			sprintf(
				$this->_exportEndpoint . '/vypis_fotografii.html?jmeno=%s&heslo=%s&id=%d&prefix=%s&id_nemovitosti=%d',
				urlencode($this->_login),
				urlencode(md5($this->_password)),
				urlencode($this->_clientId),
				urlencode($this->_prefix),
				urlencode($idNabidka)
			)
		);
		$imageListLines = explode("\n", $imageListRaw);

		$isOK = array_shift($imageListLines);

		$presentOnServer = array();
		if ($isOK == 'OK') {
			foreach ($imageListLines as $row) {
				$explodedByPipe = explode('|', $row);
				$fileNamePart = array_pop($explodedByPipe);
				$explodedByDot = explode('.', $fileNamePart);
				$remoteHash = array_shift($explodedByDot);
				$presentOnServer[$remoteHash] = true;
			}
		}

		foreach ($images as $ord=>$image) {
			if (file_exists($image['soubor'])) {
				$fileHash = md5_file($image['soubor']);
				$images[$ord]['md5'] = $fileHash;
				if (empty($presentOnServer[$fileHash])) {
					$newPath = $this->_workDir . DIRECTORY_SEPARATOR . $fileHash . '.jpg';
					copy($image['soubor'], $newPath);
					$this->_http->post(
						$this->_exportEndpoint . '/uloz_xml.html',
						array(
							'jmeno' => $this->_login,
							'heslo' => md5($this->_password),
							'id' => $this->_clientId,
							'prefix' => $this->_prefix
						),
						array(
							's' => $newPath
						)
					);
				}
			}
		}
		return $images;
	}

	/**
	 * Pošle obrázek uživatele. Pokud už jsme už poslali, znova neposíláme.
	 *
	 * @param array $userData Informace o makléři.
	 *
	 * @return bool Zda jsme poslali obrázek.
	 */
	protected function _uploadBrokerFace(array $userData)
	{
		if (!isset($this->_brokerFaceSent[$userData['makler_int_id']]) && file_exists($userData['foto'])) {

			$newPath = $this->_workDir . DIRECTORY_SEPARATOR . $userData['makler_int_id'] . '.jpg';

			copy($userData['foto'], $newPath);

			$obrazekNahrany = $this->_http->post(
				$this->_exportEndpoint . '/uloz_xml.html',
				array(
					'jmeno' => $this->_login,
					'heslo' => md5($this->_password),
					'id' => $this->_clientId,
					'prefix' => $this->_prefix
				),
				array(
					's' => $newPath
				)
			);
			if ($this->_jeOK($obrazekNahrany)) {
				$this->_brokerFaceSent[$userData['makler_int_id']] = true;
				return true;
			}
		}
		return false;
	}

	/**
	 * Vygeneruje XML soubor pro přidání/odebrání nabídky.
	 *
	 * @param int    $idNabidka   ID nabídky.
	 * @param string $akceExportu Akce - U=přidat D=odebrat.
	 * @param array  $pole        Zformátované informace o nabídce a makléři.
	 * @param array  $fotos       Pole s fotkami nabídky.
	 *
	 * @return string Výsledné XML jako řetězec.
	 */
	protected function _generateXML($idNabidka, $akceExportu, array $pole, array $fotos)
	{
		$xml = new Dalten_Xml_SimpleXml('<?xml version="1.0" encoding="utf-8"?><xreal/>');

		$identifikaceXML = $xml->addChild('identifikace');
		$identifikaceXML->addAttribute('typ', 'CLIENT');
		$identifikaceXML->addChild('datumcas', date('Y-m-d-H-i-s'));
		$identifikaceXML->addChild('export-jmeno', $this->_exportJmeno);
		$identifikaceXML->addChild('export-verze', $this->_exportVerze);
		$autorizaceXML = $identifikaceXML->addChild('autorizace');
		$autorizaceXML->addAttribute('id', sprintf('%05d', $this->_clientId));
		$autorizaceXML->addAttribute('prefix', $this->_prefix);
		$autorizaceXML->addAttribute('jmeno', $this->_login);

		$nabidkyXML = $xml->addChild('nabidky');
		$nabidkaXML = $nabidkyXML->addChild('nabidka');
		$nabidkaXML->addAttribute('id', $idNabidka);
		$nabidkaXML->addAttribute('operace', $akceExportu);

		foreach ($pole as $id=>$text) {
			if ($text!=='') {
				if ($id=='popis') {
					// ořízneme na 3000 znaků plus 20 jako rezerva
					$text = $this->_cropText($text, 2880);
				} else {
					$text = $this->_cropText($text, 980);
				}
				$nabidkaXML
					->addChild('pole', $this->_preparePoleValue($text))
					->addAttribute('id', $id);
			}
		}

		foreach ($fotos as $foto) {
			$fotoXML = $nabidkaXML->addChild('foto');
			$fotoXML->addAttribute('id', $foto['md5']);
			$fotoXML->addAttribute('popis', $foto['popis']);
		}

		return $xml->getMultilineXml();
	}

	/**
	 * Ořízne text na první mezeře po daném počtu znaků a na konec přidá trojtečku.
	 *
	 * @param string $text  Text k oříznutí.
	 * @param int    $limit Limit počtu znaků.
	 *
	 * @return string Oříznutý text.
	 */
	protected function _cropText($text, $limit)
	{
		$encoding = 'UTF-8';
		if (mb_strlen($text) > $limit) {
			// řežeme jen pokud přesáhne limit
			$prefix = mb_strcut($text, 0, $limit, $encoding);
			$suffix = mb_strcut($text, $limit, null, $encoding);
			if (preg_match('/\s+/u', $suffix) > 0) {
				// pokud část za limitem obsahuje mezeru ořízeneme na této mezeře
				preg_match('/(\S+)/u', $suffix, $matches);
				return $prefix . $matches[1] . '...';
			}
			// pokud za limitem mezera není ořízneme hned
			return $prefix . '...';
		}
		return $text;
	}

	/**
	 * Připraví hodnotu pole na export.
	 *
	 * @param mixed $value Hodnota pole.
	 *
	 * @return string Hodnota pole připravená na export.
	 */
	protected function _preparePoleValue($value)
	{
		if (is_float($value)) {
			return sprintf('%F', $value);
		}
		$value = str_replace('&', '&#38;', $value);
		return $this->_escapeQuotes($value);
	}

	/**
	 * Ošetří uvozovky (přidá před ně zpětné lomítko).
	 *
	 * @param string $text Text na ošetření.
	 *
	 * @return string Ošetřený text.
	 */
	protected function _escapeQuotes($text)
	{
		return str_replace(array('"', "'"), array('\"', "\\'"), $text);
	}

	/**
	 * Přeloží jejich odpověď serveru na naši.
	 *
	 * @param string $response Odpověď jejich serveru.
	 *
	 * @return Dalten_Export_Api_ServerResponse Přeložená odpověď.
	 */
	protected function _handleResponse($response)
	{
		$responseLines = explode("\n", $response);
		if ($responseLines[0] == 'OK') {
			return new Dalten_Export_Api_ServerResponse(true);
		} else {
			return new Dalten_Export_Api_ServerResponse(
				false,
				500,
				iconv('windows-1250', 'UTF-8', (isset($responseLines[1]) ? $responseLines[1] : ''))
			);
		}
	}

	/**
	 * Vrátí zda je předaná odpověť OK.
	 *
	 * @param string $response Odpověď ke kontrole.
	 *
	 * @return bool Zda je odpověď OK.
	 */
	protected function _jeOK($response)
	{
		$lines = explode("\n", $response);
		return ($lines[0] == 'OK');
	}

	/**
	 * Nastaví logger pro backend.
	 * Logger bude použit pouze pokud to backend dovoluje.
	 *
	 * @param \Dalten_Export_Api_Backend_Logger_LoggerInterface $logger Instance loggeru.
	 *
	 * @return Dalten_Export_Api_LoggableApiInterface Fluent interface.
	 */
	public function setLogger(Dalten_Export_Api_Backend_Logger_LoggerInterface $logger)
	{
		if ($this->_http instanceof Dalten_Export_Api_Backend_LoggableBackendInterface) {
			$this->_http->setLogger($logger);
		}
		$this->_logger = $logger;
		return $this;
	}

	/**
	 * Odstraní nastavený logger pro backend.
	 *
	 * @return Dalten_Export_Api_LoggableApiInterface Fluent interface.
	 */
	public function removeLogger()
	{
		if ($this->_http instanceof Dalten_Export_Api_Backend_LoggableBackendInterface) {
			$this->_http->removeLogger();
		}
		$this->_logger = null;
		return $this;
	}


}
