<?php

namespace app\Common;

use Carbon\Carbon;
use Config;
use DB;
use File;
use Hash;
use Lang;
use Request;
use Route;
use URL;

// class with all application common functions
class CommonLib
{
    // declarations
    private $skip_default = [];
    private $system_control_prefix = '_';
    private $file_control_prefix = 'f_';

    // set common multi-dimensional control select item separator sign
    private $controlSelectSeparatorSign = '+ ';

    // clear variable from tags and trims
    public function clear($string, $strip_tags = true, $allowed_tags = '')
    {
        $data = '';
        if (is_array($string)) {
            foreach ($string as $key => $value) {
                if ($strip_tags === true) {
                    $data[$key] = strip_tags(trim($value), $allowed_tags);
                } else {
                    $data[$key] = trim($value);
                }
            }
        } else {
            if ($strip_tags === true) {
                $data = strip_tags(trim($string), $allowed_tags);
            } else {
                $data = trim($data);
            }
        }

        return $data;
    }

    // get pagination data
    public function pagination($page = 1, $limit = 10, $url = null, $count = 0)
    {
        // get pagination details
        $data = [];
        $data['count'] = $count;
        $data['current_page'] = $page;
        $data['limit'] = $limit;
        $data['number_of_pages'] = $number_of_pages = round(($count / $limit) + 0.49);
        // generate pagination data
        $data['items'] = [];
        for ($i = 0; $i < $number_of_pages; ++$i) {
            // skip some pages if count is large
            if ($number_of_pages > 10) {
                if (
                    ($i > 2) &&
                    ($i < ($number_of_pages - 3)) &&
                    (($i < ($page - 2)) ||
                        ($i > $page)) &&
                    ((($i + 1) % 10) != 0)
                ) {
                    continue;
                }
            }
            // add item
            $data['items'][] = [
                'number' => ($i + 1),
                'link' => $url . '/' . ($i + 1),
            ];
        }
        // return
        return $data;
    }

    // get system path
    public function systemPath($url, $public = true)
    {
        if ($public === true) {
            $url = 'public/' . ltrim($url, '/');
        }

        return base_path($url);
    }

    // percents format
    public function percentFormat($percent)
    {
        return $percent . '%';
    }

    // determine menu pages link
    public function pageLink($link)
    {
        return "/" . $link->slug;
    }

    // get unexpected error
    public function unexpectedError(&$data, $custom_message = null)
    {
        $data['success'] = false;
        $data['unexpected_error'] = true;
        // check message
        if (!is_null($custom_message)) {
            $data['errors'] = $custom_message;
            return true;
        }
        // set default unexpected error
        $data['errors'] = trans('exception.unexpected_error');
    }

    // verify google invisible captcha
    public function grecaptchaVerify($token)
    {
        // get url
        $url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . config('app.grecaptcha_secret_key') . '&response=' . $token . '&remoteip=' . $_SERVER['REMOTE_ADDR'];
        // get content
        $content = json_decode(file_get_contents(urldecode($url)));
        // validate
        return (isset($content->success) && true === $content->success) ? true : false;
    }

    // generate url slug from input string
    public function makeSlug($str)
    {
        // lowercase string
        $str = mb_strtolower($str, 'UTF-8');

        // list of allwed chars
        $allowed = [
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-',
        ];
        // important non-allowed characters to replace with
        $replace = [
            'č', 'ć', 'ž', 'š', 'đ', ' ', '&',
        ];
        // replacement for important non-allowed characters
        $replaceWith = [
            'c', 'c', 'z', 's', 'dj', '-', '-i-',
        ];

        // replace characters
        $str = str_replace($replace, $replaceWith, $str);
        $count = strlen($str);

        $new = '';
        // remove disallowed chars
        for ($i = 0; $i < $count; ++$i) {
            // check if current char in allowed list
            if (in_array($str[$i], $allowed, true)) {
                // skip multiple dashes in row
                if ($str[$i] == '-' && $new[strlen($new) - 1] == '-') {
                    continue;
                }
                $new .= $str[$i];
            }
        }

        return $new;
    }

    // check database slug availability and call loop till available slug found
    public function checkSlug($table, $column, $slug, $minify = false, $pid)
    {
        $counter = 1;
        $rand_chars = [
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
        ];
        $rand_chars_length = count($rand_chars) - 1;
        if ($minify === true) {
            // if value zero then add N letter in front of that otherwise key => value will be difficult with zero in key place
            if ($slug[0] === 0) {
                $slug = 'N' . $slug;
            }
            // if minify true then minify slug
            $slug = $this->minifySlug($slug);
        }
        if ($pid == 14) {
            $new_slug = 'kreditni-proizvodi/' . $slug;
        } else {
            $new_slug = $slug;
        }

        // check if slug exist
        $count = DB::table($table)
            ->where($column, $slug)
            ->count();

        // if slug exist try again adding counter value to end of slug
        while ($count != 0) {
            // if minify change method of concatenation
            if ($minify === true) {
                $new_slug = $slug . $rand_chars[rand(0, $rand_chars_length)];
                /*
                 * bug prevention. if there are more items with same name than available characters eg. 50+
                 * then we insert two random chars so we can generate unique slug
                 */
                if ($counter > 52) {
                    $new_slug .= $rand_chars[rand(0, $rand_chars_length)];
                }
            } else {
                $new_slug = $slug . '-' . $counter;
            }
            $count = DB::table($table)
                ->where($column, $new_slug)
                ->count();
            ++$counter;
        }
        // return generated slug
        return $new_slug;
    }

    // minify slug. leave just beginning letters of each word. remove dashed.
    public function minifySlug($slug, $minify_length = 0, $each_word_part_length = 3)
    {
        // minify only if string larger than minify_length
        if (strlen($slug) > $minify_length) {
            $words = explode('-', $slug);
            $slug = '';
            foreach ($words as $word) {
                for ($i = 0; $i < $each_word_part_length; ++$i) {
                    if (isset($word[$i])) {
                        $slug .= $word[$i];
                    }
                }
            }
        }
        // remove all dashes
        $slug = str_replace('-', '', $slug);
        // retrun minified slug
        return $slug;
    }

    // check database token availability and call loop till available token found
    public function makeToken($table, $column, $hash = true, $token_length = 40)
    {
        // get init token
        if ($hash === false) {
            $token = str_random($token_length);
        } else {
            $token = Hash::make(str_random($token_length));
        }

        // check if slug exist
        $count = DB::table($table)
            ->where($column, $token)
            ->count();

        // if token exist try another one
        while ($count != 0) {
            // regenerate token
            if ($hash === false) {
                $token = str_random($token_length);
            } else {
                $token = Hash::make(str_random($token_length));
            }
            $count = DB::table($table)
                ->where($column, $token)
                ->count();
        }

        return $token;
    }

    // check file existance and call loop till available filename found
    public function checkFile($destination, $filename)
    {
        $counter = 1;
        $file = rtrim($destination, '/') . '/' . $filename;
        $parts = explode('.', $filename);

        // check if slug exist
        $exist = File::exists($file);

        // if slug exist try again adding counter value to end of slug
        while ($exist == true) {
            // get new filename
            $filename = $parts[0] . '-' . $counter . '.' . $parts[1];
            $file = rtrim($destination, '/') . '/' . $filename;

            // check if slug exist
            $exist = File::exists($file);
            ++$counter;
        }

        return $filename;
    }

    // get images
    public function getImages($path, $image, $thumb = 'thumbs', $absolute_path = false)
    {
        // set default filename
        $image_default = 'profile.jpg';
        if ($path == 'assets/files/backgrounds/') {
            $image_default = 'profile.jpg';
        }
        // check path slash
        if ($path[strlen($path) - 1] != '/') {
            $path .= '/';
        }
        // if image empty
        $image = trim($image);
        if (empty($image)) {
            $image = $image_default;
        }
        // trim image
        $image = ltrim(rtrim($image, '/'), '/');
        $files = [];
        // check if thumb required
        if ($thumb != false) {
            $image_thumb = ltrim(rtrim($thumb, '/'), '/') . '/' . ltrim(rtrim($image, '/'), '/');
        }
        // get system path
        $sys_image = $this->systemPath($path . $image);
        // if thumb required
        if (!is_null($thumb) && $thumb != false) {
            $sys_image_thumb = $this->systemPath($path . $image_thumb);
        }
        // check if file exists
        if (file_exists($sys_image)) {
            // check if thumb exists otherwise set default image
            if ($thumb != false) {
                if (!file_exists($sys_image_thumb)) {
                    $image_thumb = ltrim(rtrim($thumb, '/'), '/') . '/' . ltrim(rtrim($image_default, '/'), '/');
                }
            }
            // if thumb required
            if (!is_null($thumb) && $thumb != false) {
                $files = [
                    'image' => $path . $image,
                    'image_thumb' => $path . $image_thumb,
                    'image_system' => $sys_image,
                    'default' => false,
                ];
            } else {
                $files = [
                    'image' => $path . $image,
                    'image_system' => $sys_image,
                    'default' => false,
                ];
            }
        } else {
            // if thumb required
            if (!is_null($thumb) && $thumb != false) {
                $image_thumb = ltrim(rtrim($thumb, '/'), '/') . '/' . ltrim(rtrim($image_default, '/'), '/');
                $files = [
                    'image' => $path . $image_default,
                    'image_thumb' => $path . $image_thumb,
                    'default' => true,
                ];
            } else {
                $files = [
                    'image' => $path . $image_default,
                    'default' => true,
                ];
            }
        }

        // return
        return $files;
    }

    // published status array
    public function statusPublished($status)
    {
        $data = [];
        switch ($status) {
            case '0':
                $data['class'] = 'warning';
                $data['name'] = Lang::get('Neaktivno');
                break;

            case '1':
                $data['class'] = 'success';
                $data['name'] = Lang::get('Aktivno');
                break;
        }

        return $data;
    }

    // check if URL segment is actual admin section
    // necessery for some file autoloads
    public function isAdmin()
    {
        if (Request::segment(1) == Config::get('app.admin')) {
            return true;
        } else {
            return false;
        }
    }

    // prepare array for db insert or update. default remove _token variable but can be passed array of keys to be removed
    public function postToDB($arr, $remove = [], $token = true, $timestamps = true)
    {
        // get item count
        $count = count($arr);

        // remove defaults
        for ($i = 0; $i < count($this->skip_default); ++$i) {
            if (array_key_exists($this->skip_default[$i], $arr)) {
                unset($arr[$this->skip_default[$i]]);
            }
        }

        // remove file controls
        foreach ($arr as $key => $value) {
            // skip system - begin with '_'
            if (substr($key, 0, 1) === $this->system_control_prefix) {
                unset($arr[$key]);
            }
            // skip files - begin with 'f_'
            if (substr($key, 0, 2) === $this->file_control_prefix) {
                unset($arr[$key]);
            }
        }

        // if token true then remove token too
        if ($token == true) {
            if (isset($arr['_token'])) {
                unset($arr['_token']);
            }
        }

        // if timestamps true then add timestamps to array
        if ($timestamps == true) {
            if (!isset($arr['created_at'])) {
                $arr['created_at'] = Carbon::now();
            }
            if (!isset($arr['updated_at'])) {
                $arr['updated_at'] = Carbon::now();
            }
        }

        // check passed array and remove named one
        foreach ($remove as $item) {
            if (isset($arr[$item])) {
                unset($arr[$item]);
            }
        }

        return $arr;
    }

    // convert empty vars to null
    // skip array providedd
    public function emptyToNull($data, $skip = [])
    {
        if (is_array($data)) {
            foreach ($data as $key => $value) {
                if ($data[$key] == '' and !in_array($key, $skip)) {
                    $data[$key] = null;
                }
            }
        } else {
            if ($data === '') {
                return;
            }
        }
        // return
        return $data;
    }

    // get file control prefix
    public function getFileControlPrefix()
    {
        return $this->file_control_prefix;
    }

    // prepare select control. input id key and value key of array
    public function getSelectArray($arr, $pick = true, $key = 'id', $value = 'name', $value_array_separator = ' ')
    {
        $data = [];

        if ($pick == true) {
            $data[''] = Lang::get('Odaberite');
        }
        // loop array
        foreach ($arr as $item) {
            // check if item is array or object
            if (is_array($item)) {
                $data[$item[$key]] = $item[$value];
            } elseif (is_object($item)) { // check if object
                if (is_array($value)) { // check if value array
                    // set first array value
                    $data[$item->$key] = $item->{$value[0]};
                    // loop estimate array values
                    for ($i = 1; $i < (count($value)); ++$i) {
                        // append
                        $data[$item->$key] .= $value_array_separator . $item->{$value[$i]};
                    }
                } else {
                    //value is not array just append
                    $data[$item->$key] = $item->$value;
                }
            }
        }
        // return
        return $data;
    }

    // generate page meta description
    public function metaGenerator($origin, $fallback_string = null)
    {
        // remove tags and leading spaces
        $origin = $this->clear($origin);
        $fallback_string = $this->clear($fallback_string);
        $break = 160;
        $min_limit = 30;
        // try origin description
        if (strlen($origin) > $min_limit) {
            // return origin
            return $origin;
        } elseif (strlen($fallback_string) > $min_limit) {
            $pos = strpos($fallback_string, '.');
            // check if dot exist and is less than 160 characters
            if ($pos > $min_limit && $pos <= $break) {
                return substr($fallback_string, 0, ($pos + 1));
            }
            $pos = strpos($fallback_string, '?');
            // check if dot exist and is less than 160 characters
            if ($pos > $min_limit && $pos <= $break) {
                return substr($fallback_string, 0, ($pos + 1));
            }
            $pos = strpos($fallback_string, '!');
            // check if dot exist and is less than 160 characters
            if ($pos > $min_limit && $pos <= $break) {
                return substr($fallback_string, 0, ($pos + 1));
            }
            // otherwise try to get any dot in text
            if (strlen($fallback_string) > $break) {
                $pos = strpos($fallback_string, '.', $break);
                // check if dot exist and is less than 160 characters
                if ($pos > 0) {
                    return substr($fallback_string, 0, ($pos + 1));
                }
                $pos = strpos($fallback_string, '?', $break);
                // check if dot exist and is less than 160 characters
                if ($pos > 0) {
                    return substr($fallback_string, 0, ($pos + 1));
                }
                $pos = strpos($fallback_string, '!', $break);
                // check if dot exist and is less than 160 characters
                if ($pos > 0) {
                    return substr($fallback_string, 0, ($pos + 1));
                }
            }
        }
        // return default
        return trans('common.default_page_description');
    }

    // return control select multi-dimensional separator sign formed for current level
    public function getSelectSeparatorSign($level = 1)
    {
        $data = '';
        for ($i = 0; $i < $level; ++$i) {
            $data .= $this->controlSelectSeparatorSign;
        }
        // append symbols
        $data .= '↓&nbsp;&nbsp;';
        // return
        return $data;
    }

    // debug pre function
    public function pre($data, $die = true)
    {
        echo '<pre>';
        print_r($data);
        echo '</pre>';
        if ($die == true) {
            die();
        }
    }

    // format price
    public function priceFormat($price, $currency = true, $dashed = false)
    {
        // display currency sign
        if (true === $currency) {
            // if dashed
            if (true === $dashed) {
                return number_format(($price / 100), 2, ',', '.') . '.- KM';
            }

            return number_format(($price / 100), 2, ',', '.') . ' KM';
        }
        // return without currency sign
        return number_format(($price / 100), 2, ',', '.');
    }

    // prepare price type sign
    public function priceSign($sign, $price, $currency, $dashed)
    {
        switch ($sign) {
            case 'percent':
                return $price . '%';
                break;
            default:
                return $this->priceFormat($price, $currency, $dashed);
                break;
        }
    }
}
