<?php

class DefaultController extends Controller
{

    /**
     * @var string the default layout for the views. Defaults to '//layouts/column2', meaning
     * using two-column layout. See 'protected/views/layouts/column2.php'.
     */
    public $layout = '//layouts/column1';
    private $dropDownCache = null;
    private $gatewayIDCache = null;

    /**
     * @return array action filters
     */
    public function filters()
    {
        return array(
            'accessControl', // perform access control for CRUD operations
        );
    }

    /**
     * Specifies the access control rules.
     * This method is used by the 'accessControl' filter.
     * @return array access control rules
     */
    public function accessRules()
    {
        return array(
            //      array('allow',
            //   'actions'=>array('test'),
            //   'expression'=>AccessManager::checkSessionPermission('addressbookAddresses'),
            //      ),
            array('allow', // allow all users to perform 'index' and 'view' actions
                'actions' => array('index', 'selectColumns', 'import', 'ajaxClearTempFolder', 'ajaxGetNextPreviewPage'),
                //'users'=>array('@'),
                'expression' => AccessManager::checkSessionPermission('addressbookAddresses'), // FIXME ! must also work for Users now
            ),
            array('deny', // deny all users
                'users' => array('*'),
            ),
        );
    }

    public function actionIndex()
    {
        $params = array_merge(func_get_args(), $_REQUEST['params']);
        $this->render('selectFile', array(
            'model' => $params['model'],
            'returnUrl' => $params['returnUrl'],
            'params' => $params,
            'helpBtnUrl' => "addressbookImportExport-index_" . strtolower($params['model'])
                )
        );
    }

    public function actionSelectColumns($model, $returnUrl)
    {
        $allowedParams = $this->getAllowedParams();
        $params = array_merge($_REQUEST);

        foreach ($params as $key => $value) {
            if (!in_array($key, $allowedParams)) {
                unset($params[$key]);
            }
        }
        $fileName = $this->saveCsvFileToRuntimeDir();
        if (!isset($fileName) || !file_exists($fileName)) {
            throw new CHttpException(404, 'file not found');
        }

        $offset = 0;
        $csvData = $this->readCsvFile($fileName, $offset);

        $dataProvider = $this->getPreviewDataProvider($model, $csvData);
        if (!$dataProvider) {
            unlink($fileName);
            die('!$dataProvider');
            throw new CHttpException(406, __('INVALID_CSV_DATA'));
        }

        $this->render('selectColumns', array(
            'dataProvider' => $dataProvider,
            'returnUrl' => $returnUrl,
            'model' => $model,
            'file' => $fileName,
            'params' => $params,
            'offset' => $offset,
            'helpBtnUrl' => "addressbookImportExport-index_" . strtolower($model)
                )
        );
    }

    public function getAllowedParams()
    {
        // FIXME : get this stuf from model
        return array('contact_tid', "group", 'csvImported', 'gw_id');
    }

    public function saveCsvFileToRuntimeDir()
    {
        $fileName = null;
        $uploadedFile = CUploadedFile::getInstanceByName('file');
        $upload_max_filesize = $this->getUploadedFileSizeInBytes(ini_get('upload_max_filesize'));

        if (!$uploadedFile) {
            throw new CHttpException(406, __('INVALID_CSV_DATA'));
        }

        if ($upload_max_filesize < $uploadedFile->size) {
            throw new CHttpException(406, 'Uploaded File is to large');
        }

        $filename = $uploadedFile->getTempName();
        if (!is_dir(app()->getRuntimePath() . '/csv')) {
            mkdir(app()->getRuntimePath() . '/csv');
        }
        $destination = app()->getRuntimePath() . '/csv/import-data' . uniqid() . '.csv';

        if (move_uploaded_file($filename, $destination)) {
            return $destination;
        }
    }

    public function getUploadedFileSizeInBytes($val)
    {
        $val = trim($val);
        $last = strtolower($val[strlen($val) - 1]);
        switch ($last) {
            // The 'G' modifier is available since PHP 5.1.0
            case 'g':
                $val *= 1024;
            case 'm':
                $val *= 1024;
            case 'k':
                $val *= 1024;
        }
        return $val;
    }

    public function readCsvFile($filePath, &$offset = 0, $limit = 5, $encoding = 'utf8')
    {
        $byteLimit = 1024;
        $data = array();
        $headerOffset = 0;
        if ($offset > 0) {
            $data = array_merge($data, $this->import($filePath, $byteLimit, $headerOffset, $encoding));
        }

        while (count($data) < $limit) {
            $nextData = $this->import($filePath, $byteLimit, $offset, $encoding);
            if (!$nextData) {
                break;
            }
            $data = array_merge($data, $nextData);
        }
        return $data;
    }

    public function import($filename, $limit = 1024, &$offset = 0, $encoding = 'utf8')
    {
        $delimiter = $this->getDelimiter($filename);
        if (!$delimiter) {
            unlink($filename);
            throw new CHttpException(406, __('INVALID_CSV_DATA'));
        }

        $handle = fopen($filename, 'rb');

        if ($handle) {
            $eof = feof($handle);
            if (!$eof) {
                fseek($handle, $offset);
                $content = '';
                $content.= fread($handle, $limit);
                $offset = $offset + $limit;

                $start = $end = '"';

                $nextChar = fread($handle, 1);
                $offset++;
                $run = 0;
                while ($run < 10000) {
                    $run++;
                    $content.= $nextChar;
                    $nextChar = fread($handle, 1);
                    $offset++;
                    // check for newLine Character
                    if ($nextChar == "\n" || $nextChar == "\r\n") {
                        $newLine = $nextChar;
                        break;
                    }
                }

                if (!isset($newLine)) {
                    if (strpos($content, "\n")) {
                        $newLine = "\n";
                    } else if (strpos($content, "\r\n")) {
                        $newLine = "\r\n";
                    }
                }
                if (isset($newLine)) {
                    $content = $this->encodeToUtf8($content, $encoding);
                    $content = explode($newLine, $content);
                }
                $data = array();
                if (is_array($content)) {
                    foreach ($content as $line) {
                        $explode = explode($delimiter, $line);
                        //array_pop($explode);
                        $data[] = $explode;
                    }
                }
            } else {
                fclose($handle);
                return false;
            }
        }
        fclose($handle);
        return $data;
    }

    public function getDelimiter($filePath)
    {
        if (!file_exists($filePath)) {
            throw new CHttpException(404, 'file not found');
        }

        $line1 = '';
        $handle = fopen($filePath, 'rb');
        $run = 0;
        while ($run < 10000) {
            $nextChar = fread($handle, 1);
            if ($nextChar != "\n" && $nextChar != "\r\n") {
                $line1.=$nextChar;
            } else {
                break;
            }
            $run++;
        }
        $line2 = '';
        $run = 0;
        $stopRecording = false;
        while ($run < 10000) {
            $nextChar = fread($handle, 1);
            if ($nextChar != "\n" && $nextChar != "\r\n") {
                if ($nextChar == '"' && !$stopRecording) {
                    $stopRecording = true;
                } else if ($nextChar == '"') {
                    $nextChar = fread($handle, 1);
                    $run++;
                    $stopRecording = false;
                }
                if (!$stopRecording) {
                    $line2.=$nextChar;
                }
            } else {
                break;
            }
            $run++;
        }
        fclose($handle);

        $allowedDelimiters = array(';', ',', "\t");
        foreach ($allowedDelimiters as $delimiter) {
            if ($line2 != '') {

                $line1SubStrCount = substr_count($line1, $delimiter);
                if ($line1SubStrCount != 0) {

                    if ($line1SubStrCount == substr_count($line2, $delimiter)) {
                        return $delimiter;
                    }
                }
            } else {
                if (strpos($line1, $delimiter) !== false) {

                    return $delimiter;
                }
            }
        }
    }

    public function encodeToUtf8($string, $encodingStandard, $newLine = "\n")
    {
        $string = mb_convert_encoding($string, 'utf-8', $encodingStandard);
        $string = str_replace("\"", '', $string);
        while (strpos($string, '  ') !== false) {
            $string = str_replace("  ", ' ', $string);
        }
        return $string;
    }

    public function getPreviewDataProvider($model, $csvData)
    {
        $rawData = array();
        $index = 0;
        if ($csvData) {
            while ($index < count($csvData[0])) {
                $test = array();
                foreach ($csvData as $values) {
                    if (isset($values[$index])) {
                        $test[] = $values[$index];
                    }
                }
                $rawData[$index] = $test;
                $index++;
            }

            for ($i = 0; $i < count($rawData); $i++) {
                if ($csvData[0][$i] != '') {

                    $attribute1 = isset($csvData[1][$i]) ? $csvData[1][$i] : '';
                    $attribute2 = isset($csvData[2][$i]) ? $csvData[2][$i] : '';
                    $attribute3 = isset($csvData[3][$i]) ? $csvData[3][$i] : '';
                    $data[] = array(
                        'attributeName' => $csvData[0][$i],
                        'model' => $model,
                        'id' => $i,
                        'attributeExemple_1' => $attribute1,
                        'attributeExemple_2' => $attribute2,
                        'attributeExemple_3' => $attribute3,
                    );
                }
            }

            foreach ($data as $key => $values) {
                foreach ($values as $valueKey => $value) {
                    $data[$key][$valueKey] = str_replace("\\n", "\n", $value);
                    $data[$key][$valueKey] = str_replace("\\r\\n", "\r\n", $data[$key][$valueKey]);
                }
            }

            if (!isset($data)) {
                return NULL;
            }
            $dataProvider = new CArrayDataProvider($data, array('pagination' => false));
            return $dataProvider;
        }
    }

    public function renderAttributesDropdown($data, $row)
    {
        $model = $data['model'];
        if ($this->dropDownCache == null) {
            $this->dropDownCache = $this->getCsvAttributes($model);
        }
        $this->renderPartial('attributesDropdown', array('model' => $model, 'row' => $row, 'lData' => $this->dropDownCache, 'data' => $data));
    }

    public function getCsvAttributes($model)
    {
        $listData = array('' => '---');
        $modelAttributes = $this->getCsvRelevantAttributes($model);

        $model = new $model();
        foreach ($modelAttributes as $attribute => $options) {
            $modelAttributes[$attribute] = $model->getAttributeLabel($attribute);
        }
        $listData = array_merge($listData, $modelAttributes);
        return $listData;
    }

    private function getCsvRelevantAttributes($model)
    {
        if (method_exists($model, 'csvRelevantAttributes')) {
            return $model::csvRelevantAttributes($model);
        }
        throw new CHttpException(403, 'Not allowed to import');
    }

    public function actionImport()
    {
        $data['sort'] = $_POST['sort'];
        $data['fileSize'] = $_POST['fileSize'];
        $data['offset'] = $_POST['offset'];
        $data['fileName'] = $_POST['fileName'];
        $data['limit'] = $_POST['limit'];
        $data['model'] = $_POST['model'];
        $data['encoding'] = $_POST['encoding'];
        $data['skipFirstLine'] = $_POST['skipFirstLine'];
        $data['params'] = is_array($_POST['params']) ? $_POST['params'] : (array) json_decode($_POST['params']);
        $attributeCount = count($data['sort']);
        $firstLine = array();
        if ($data['offset'] == '0' & $data['skipFirstLine'] == 'true') {
            $firstLine = $this->import($data['fileName'], $data['limit'], $data['offset']);
            unset($firstLine[0]);
        }

        $csvData = $this->import($data['fileName'], $data['limit'], $data['offset'], $data['encoding']);

        if ($csvData) {
            $csvData = array_merge($firstLine, $csvData);
        } else {
            $csvData = $firstLine;
        }
        //FIXME
        if (empty($csvData)) {
            $data['eof'] = true;
        }
        if ($data['offset'] >= $data['fileSize']) {
            $data['eof'] = true;
        }

        foreach ($csvData as $attributeValues) {
            if (empty($attributeValues)) {
                continue;
            }
            $newModel = new $data['model']();
            foreach ($data['sort'] as $row => $value) {
                if ($row === 'delimiter' || $row === 'encodingStandard' || $value == '') {
                    continue;
                }
                if (!isset($attributeValues[$row])) {
                    continue;
                }

                $attributeValue = $attributeValues[$row];
                $attributeValue = trim($attributeValue);

                $pos = strpos($attributeValue, '\n');
                if ($pos !== FALSE) {
                    $attributeValue = str_replace('\n', "\n", $attributeValue);
                }
                $pos = strpos($attributeValue, '\r\n');
                if ($pos !== FALSE) {
                    $attributeValue = str_replace('\r\n', "\n", $attributeValue);
                }
                $newModel->$value = $attributeValue;
            }

            $modelValues = array_filter($newModel->attributes);
            if (!empty($modelValues)) {
                //add Custom Values Like contact_tid for addresses Models
                foreach ($data['params'] as $key => $value) {
                    $newModel->$key = $value;
                }
                $this->validateImportOptions($newModel);
                if (property_exists(get_class($newModel), "source")) {
                    $newModel->source = AddressSourceType::CSV_IMPORT;
                }

                $hookData = array();
                $this->beforeImport($newModel, $hookData);

                if ($newModel instanceof User) {
                    $this->importToUserModel($newModel, $hookData);
                } else {
                    $this->importToAddressModel($newModel, $hookData);
                }
            }
        }
        if (array_key_exists('contact_tid', $data['params'])) {
            $this->initiateSyncForEachContactList($data['params']['contact_tid']);
        }
        echo json_encode($data);
    }

    private function importToUserModel($newModel, $hookData)
    {
        if ($newModel->validate()) {
            $newModel->save(false);
            $this->afterImport($newModel, $hookData);
        } else {
            foreach ($newModel->errors as $errorAttributeName => $error) {
                Log::w($errorAttributeName . ' : ' . $error[0], 'phonalisa-csv-import');
            }
        }
    }

    private function importToAddressModel($newModel, $hookData)
    {
        if (!$newModel->save()) {
            foreach ($newModel->errors as $errorAttributeName => $error) {
                Log::w($errorAttributeName . ' : ' . $error[0], 'phonalisa-csv-import');
            }
        } else {
            $this->afterImport($newModel, $hookData);
        }
    }

    private function initiateSyncForEachContactList($addressBookID)
    {
        $iCloudContactsLists = iCloudContactsList::model()->findAllByAttributes(array('plDirectoryId' => $addressBookID));
        if (!empty($iCloudContactsLists)) {
            foreach ($iCloudContactsLists as $iCloudContactsList) {
                if (isset($iCloudContactsList)) {
                    $iCloudContactsList->startICloudSync();
                }
            }
        }
    }

    private function beforeImport($newModel, &$data = array())
    {
        if ($newModel instanceof User) {
            $newModel->isSystemAdmin = null;
            $password = $this->getDefaultPassword();
            $newModel->password = $password;
            $data["password"] = $password;
        }

        if ($newModel instanceof RoutingOutbound) {
            $newModel->gw_id = $this->getGatewayID();
        }

        if ($newModel instanceof RoutingInbound) {
            if ($newModel->targetType !== (string) TargetType::CallThrough && $newModel->targetType !== (string) TargetType::Manual) {
                $newModel->targetType = '';
                $newModel->target = '';
            }
        }
    }

    private function getDefaultPassword()
    {
        $defaultPassword = cfg("DEFAULT_NEW_USER_PASSWORD");
        if (empty($defaultPassword)) {
            $defaultPassword = PasswordGenerator::generateUserPassword();
        }
        return $defaultPassword;
    }

    private function afterImport($newModel, &$data = array())
    {
        if ($newModel instanceof User) {
            $updateModel = User::model()->findByPk($newModel->id);
            $updateModel->unsetAttributes(array('password'));
            $updateModel->password = $data["password"];
            $updateModel->addressReferece = $this->createInternAddress($updateModel->attributes);
            $updateModel->save(false);
        }
    }

    private function getGatewayID()
    {
        if ($this->gatewayIDCache === null) {
            $gateways = Gateway::model()->findAll(array());
            $sortedGateways = RoutingOutbound::model()->sortedGateways($gateways);
            $useGatewayID = '';
            if (!empty($sortedGateways['active'])) {
                $useGatewayID = $sortedGateways['active'][0]['id'];
            } else {
                $useGatewayID = $sortedGateways['disabled'][0]['id'];
            }
            $this->gatewayIDCache = $useGatewayID;
        }
        return $this->gatewayIDCache;
    }

    private function createInternAddress($newAttributes)
    {
        //creating intern addressbook entry for this user
        $internAddressbook = Addressbook::model()->findByAttributes(array('type' => AddressbookType::intern));
        if ($internAddressbook) {
            $internAddress = new Addresses();
            if ($newAttributes['firstname'] . $newAttributes['lastname'] === "") {
                $internAddress->n_given = $newAttributes['name'];
            } else {
                $internAddress->n_given = $newAttributes['firstname'];
                $internAddress->n_family = $newAttributes['lastname'];
            }
            $internAddress->contact_owner = 'intern';
            $internAddress->contact_created = time();
            $internAddress->contact_email = $newAttributes['email'];
            $internAddress->contact_tid = $internAddressbook->id;
            $internAddress->tel_work = $newAttributes['extension'];
            $internAddress->source = AddressSourceType::SYSTEM;
            if ($internAddress->save()) {
                return $internAddress->id;
            }
        }
    }

    public function validateImportOptions(&$model)
    {
        foreach ($this->getCsvRelevantAttributes($model) as $attribute => $options) {
            foreach ($options as $option => $value) {
                if ($option == 'length') {
                    if (substr($model->$attribute, 0, $value) != false) {
                        $model->$attribute = substr($model->$attribute, 0, $value);
                    }
                }
                if ($option == 'relation') {
                    if ($model->$attribute == '' || $model->$attribute == null) {
                        continue;
                    }
                    $relationModel = $value['model']::model()->findByAttributes(array($value['value'] => $model->$attribute));
                    if (!$relationModel) {
                        $relationModel = new $value['model']();
                        $relationModel->{$value['value']} = $model->$attribute;
                        if (!$relationModel->save()) {
                            throw new CHttpException(500, 'could not save new Relation Object');
                        }
                    }
                    $model->$attribute = $relationModel->id;
                }
            }
        }
    }

    public function readSourceFile()
    {
        $uploadedFile = CUploadedFile::getInstanceByName('file');
        $filePath = $uploadedFile->getTempName();
        $contacts = $this->readCsvFile($filePath);
        return $contacts;
    }

    public function actionAjaxClearTempFolder()
    {
        $tempPath = $_POST['fileName'];
        $path = realpath($tempPath);
        if (is_readable($path)) {
            unlink($path);
        }
    }

    public function actionAjaxGetNextPreviewPage()
    {
        $fileName = $_REQUEST['fileName'];
        $page = $_REQUEST['page'];
        $result['currentPage'] = $page;
        if ($page < 1) {
            $page = 1;
        }

        $data = $this->readCsvFile($fileName, $offset, 100, $_POST['encoding']);

        if ($page + 3 > count($data)) {
            $page = count($data) - 3;
        }

        $pageData = array(
            $data[0],
            $data[$page++],
            $data[$page++],
            $data[$page++],
        );
        $result['data'] = $pageData;
        $result['page'] = $page;

        echo json_encode($result);
    }

    public function getDefaultValueForAttributeDropdown($attributeName, $modelName)
    {
        $score = null;
        $possibleValue = '';
        $model = new $modelName();
        $csvRelevantAttributes = $this->getCsvRelevantAttributes($modelName);
        $data = $model->attributes;

        foreach ($data as $modelAttributeName => $value) {
            if (!in_array($modelAttributeName, $csvRelevantAttributes)) {
                continue;
            }
            $modelAttributeLabel = $model->getAttributeLabel($modelAttributeName);
            if ($attributeName == $modelAttributeName) {
                return $attributeName;
            } else {
                $str1 = metaphone($modelAttributeLabel);
                $str2 = metaphone($attributeName);
                $thisScore = levenshtein($str1, $str2);

                if (!isset($score)) {
                    $score = $thisScore;
                }
                if ($thisScore <= $score) {

                    $possibleValue = $modelAttributeName;
                    $score = $thisScore;
                }
            }
        }
        return $possibleValue;
    }
}
