<?php
/**
 * @package    Proxim
 * @author     Dominic Tanui <dominic@codeworldweb.com | https://codeworldweb.com>
 * @copyright  2019 Proxim
 * @version    1.5.0
 * @since      File available since Release 1.0.0
 */

namespace Proxim\Order;

use Db;
use Proxim\Database\DbQuery;
use Proxim\Cache\Cache;
use Proxim\Application;
use Proxim\ObjectModel;
use Proxim\Order\File;
use Proxim\PriceCalculator;
use Proxim\Configuration;
use Proxim\Coupon;
use Proxim\Currency;
use Proxim\Preference\Discipline;
use Proxim\Preference\PaperFormat;
use Proxim\Preference\PaperType;
use Proxim\Presenter\Object\ObjectPresenter;
use Proxim\Presenter\Order\OrderFilePresenter;
use Proxim\Site\Site;
use Proxim\User\Customer;
use Proxim\User\Employee;
use Proxim\Util\ArrayUtils;
use Proxim\Util\DateUtils;
use Proxim\Validate;
use Proxim\Tools;
use Symfony\Component\DependencyInjection\Variable;

/**
 * Order
 */
class Order extends ObjectModel
{
    const START_ORDER_ID = 11233;

    /**
	 * Dispute - is the status of the order when customer is not satisfied
	 * with the quality of the paper, or this order was delivered late and
	 * customer wants to receive a refund.
	 *
	 * We have a dispute manager who deals with all the disputed orders,
	 * however Support Team Members should know basic information about it.
	 */
	const DISPUTE = 1; 
	/**
	 * When customer tries to pay for the order, but his payment doesn't
	 * go through, we receive "Fake" order.
	 *
	 * Basically, this status means that order has 96% possibility to become
	 * a sale, as customer had filled out the whole form and clicked on "Pay
	 * for the order")
	 *
	 * When you receive such order, it is your responsibility to contact
	 * customer and to help him to complete the payment.
	 */
	const FAKE = 0;
	/**
	 * Confirmation Pending - inquiry with instructions for the writer to
	 * start working on it, we just need customer to proceed with the payment.
	 */
	const CONFIRMATION_PENDING = 2;
	
	/**
	 * Writer Assigned - is a paid order that was assigned to a specific writer
	 */
	const WRITER_ASSIGNED = 6;

	/**
	 * Writer Accepted - is a paid order that was already published, and few writers applied for it.
	 * Such orders have a list of writers who are willing to work on them.
	 * At this point supporter has to assign this order to one of the writers
	 */
	const WRITER_ACCEPTED = 7;
	
	/**
	 * Done – status of the order when we receive completed paper from the writer.
	 * At this point customer doesn't have access to this order yet.
	 * Supporter needs to check this paper for plagiarism, paper format, etc.
	 *
	 * In case if there is plagiarism or this order was written in a wrong format,
	 * it should be set on "Revision" status.
	 *
	 * If the order is completed correctly, then the preview version should be sent to customer.
	 */
	const DONE = 8;
	/**
	 * Order can move to "Canceled" status at any stage.
	 * But there are several main reasons to "cancel" the order:
	 *
	 * Customer doesn't want to pay for his inquiry
	 * Customer accidentally placed 2 or more identical inquiries or orders.
	 * Writer wasn't assigned and we issued customer a full refund
	 *  
	 */
	const CANCELLED = 9;
	/**
	 * Order moves to Finished  status when customer approves the order
	 * and receives MS Word version of the paper.
	 *
	 * By approving the order, customer confirms that product is of expected quality.
	 */
	const FINISHED = 10;
	/**
	 * This status means that the Preview version of the order is available for customer
	 * and he/she can review it in .pdf format.
	 *
	 * Preview version is protected with a password,
	 * so technically customer cannot print or copy/paste it.
	 */
	const DELIVERED = 11;
	/**
	 * New Paid - is a status of a new order that we receive from
	 * customer. This is a stage when order is paid.

	* At this point order is not visible for writers.

	* You have to check such order and make sure that there is no customer's
	* personal information and that we have all required instructions.
	*/
	const NEW_PAID = 12;
	/**
	 *  Free Inquiry -  is a transitional status. When we receive it, we
	 *  have to check instructions and call customer to turn this inquiry into
	 *  a paid order (thus status will be changed to "New Paid").
	 *
	 *  In case if we can not contact customer, or he/she refuses to
	 *  complete the payment, status will be changed as well ("Reply Pending",
	 *  "Confirmation Pending", "Canceled")
	 */
	const FREE_INQUIRY = 13;
	/**
	 * Revision – Order is in a process of editing and revising.
	 *
	 * It is necessary when customer is not satisfied with the quality of a product.
	 *
	 * Also order can move to this status when plagiarism or
	 * format mistakes were found by Support Team Member.
	 */
	const REVISION = 14;
	/**
	 * Reply Pending - inquiry with the lack of instructions for us to look
	 * for the writer, thus we need to ask customer to specify/add instructions
	 * for the assignment.
	 *
	 * When we will receive such information customer can proceed with
	 * the payment.
	 */
	const REPLY_PENDING = 15;
	/**
	 * This status visually looks like "Order Published," but it is NOT
	 * PAID. Orders with such status are visible for writers.
	 *
	 * This status should be applied in following cases:
	 *
	 * - big orders over $300
	 * - difficult topic: programming for ex.
	 * - specific sources are required
	 */
	const INQUERY_PUBLISHED = 16;
	/**
	 * When writers apply for orders in "Inquiry Published" status,
	 * they move to "Inquiry Writer Accepted" status.
	 *
	 * Keep in mind that it's not paid yet, so we cannot assign this order
	 * to the writer until we will receive the payment.
	 */
	const INQUERY_WRITER_ACCEPTED = 17;
	/**
	 * Order published - is a paid order, which was checked by the supporter
	 * on personal information, deadline, attachments.
	 *
	 * At this point order is visible for all writers and they can place their bids.
	 */
	const ORDER_PUBLISHED = 19;
	const CONVERT_FAILED = 20;
	const CALL_PENDING = 21;

	// aliases
	const INQUIRY = self::FREE_INQUIRY;
	const LACK_OF_INSTRUCTIONS = self::REPLY_PENDING;

	const SPACING_SINGLE = 'single';
	const SPACING_DOUBLE = 'double';
	
	/** Services **/
	const WRITING_PAGES = 1;
	const WRITING_SLIDES = 2;
	const WRITING_CHARTS = 31;
	const CHOOSE_WRITER = 3;
	const PROVIDE_ME_SAMPLES = 4;
	const PROGRESSIVE_DELIVERY = 5;
	const DISCOUNT = 6;
	const USED_SOURCES = 21;
	const COMPLEX_ASSIGNMENT = 37;
	const COUPON_WRT_PAGES = 10;
	const REVISION_MINOR_PAGES = 26;
	const REVISION_MINOR_SLIDES = 27;
	const REVISION_MINOR_CHARTS = 33;
	const REVISION_MAJOR_PAGES = 28;
	const REVISION_MAJOR_SLIDES = 29;
	const REVISION_MAJOR_CHARTS = 35;
    const EXPERT_PROOFREADING = 41;
    const VIP_SUPPORT = 44;
    const PLAGIARISM_REPORT = 45;
    const DRAFT_OUTLINE = 46;
	const SHORTEN_DEADLINE = 42;

	const REVISION_MINOR = 42;
    const REVISION_MAJOR = 43;
    
    /** @var $id Order ID */
	public $id;
    
    /** @var string $tracking_id Tracking ID */
    public $tracking_id;
    
    /** @var int site_id */
    public $site_id = PROX_SITE_ID;

    /** @var int form_type_id */
    public $form_type_id;

    /** @var string form_type */
    public $form_type;

    /** @var string payment_method */
    public $payment_method;

    /** @var int currency_id */
    public $currency_id;

    /** @var float currency_to_usd_rate */
    public $currency_to_usd_rate;

    /** @var int gmt */
    public $gmt;

    /** @var int customer_id */
    public $customer_id;

    /** @var int writer_id */
    public $writer_id;

    /** @var int requested_writer_id */
    public $requested_writer_id;

    /** @var int editor_id */
    public $editor_id;

    /** @var int status_id */
    public $status_id = self::FAKE;

    /** @var int parent_id */
    public $parent_id;

    /** @var int service_type */
    public $service_type = self::COMPLEX_ASSIGNMENT;

    /** @var int tariff_id */
    public $tariff_id;

    /** @var string title */
    public $title;

    /** @var int academic_level_id */
    public $academic_level_id;

    /** @var int paper_type_id */
    public $paper_type_id;

    /** @var string paper_type_option */
    public $paper_type_option;

    /** @var int topic_category_id */
    public $topic_category_id;

    /** @var string topic_category_option */
    public $topic_category_option;

    /** @var bool is_complex_assignment */
    public $is_complex_assignment = 0;

    /** @var int paper_format_id */
    public $paper_format_id;

    /** @var string paper_format_option */
    public $paper_format_option;

    /** @var int writer_category_id */
    public $writer_category_id;

    /** @var int writer_percent */
    public $writer_percent = 0;

    /** @var int pages */
    public $pages = 0;

    /** @var int charts */
    public $charts = 0;

    /** @var int slides */
    public $slides = 0;

    /** @var int sources */
    public $sources = 0;

    /** @var string spacing */
    public $spacing = self::SPACING_DOUBLE;

    /** @var string paper_details */
    public $paper_details;

    /** @var string comments */
    public $comments;

    /** @var bool writer_samples */
    public $writer_samples = 0;

    /** @var bool used_sources */
    public $used_sources = 0;

    /** @var bool expert_proofreading */
    public $expert_proofreading = 0;

    /** @var bool vip_support */
    public $vip_support = 0;

    /** @var bool plagiarism_report */
    public $plagiarism_report = 0;

    /** @var bool draft_outline */
    public $draft_outline = 0;

    /** @var bool progressive_delivery */
    public $progressive_delivery = 0;

    /** @var float raw_cost */
    public $raw_cost = 0;

    /** @var float total */
    public $total = 0;

    /** @var float tax */
    public $tax = 0;

    /** @var int discount */
    public $discount = 0;

     /** @var int discount_type */
    public $discount_type = 0;

    /** @var float price_per_page */
    public $price_per_page = 0;
    
    /** @var float total_paid */
    public $total_paid = 0;

    /** @var string paid_at */
    public $paid_at;

    /** @var bool is_paid */
    public $is_paid = 0;

    /** @var float writer_pay */
    public $writer_pay = 0;

    /** @var bool is_writer_paid */
    public $is_writer_paid = 0;

    /** @var string writer_paid_at */
    public $writer_paid_at;

    /** @var bool is_writer_confirmed */
    public $is_writer_confirmed = 0;

    /** @var float price_per_page */
    public $editor_pay = 0;

    /** @var bool is_editor_paid */
    public $is_editor_paid = 0;

    /** @var string editor_paid_at */
    public $editor_paid_at;

    /** @var bool is_editor_confirmed */
    public $is_editor_confirmed = 0;
    
    /** @var int hrs_customer */
    public $hrs_customer = 0;

    /** @var string customer_deadline */
    public $customer_deadline;

    /** @var int hrs_writer */
    public $hrs_writer = 0;

    /** @var string writer_deadline */
    public $writer_deadline;

    /** @var bool writer_assigned */
    public $writer_assigned = 0;

    /** @var string writer_assigned_at */
    public $writer_assigned_at;

    /** @var string form_type */
    public $editor_assigned = 0;

    /** @var string editor_assigned_at */
    public $editor_assigned_at;

    /** @var string approved_at */
    public $approved_at;

    /** @var string completed_at */
    public $completed_at;

    /** @var string cancelled_at */
    public $cancelled_at;

    /** @var string delivered_at */
    public $delivered_at;

    /** @var string finished_at */
    public $finished_at;

    /** @var string date_upd */
    public $date_upd;

    /** @var string date_add */
    public $date_add;

    public $winbackCoupons = array();

    public $presentFiles = array();

    public $currency;
    
    /**
     * @see ObjectModel::$definition
     */
    public static $definition = array(
        'table' => 'order',
        'primary' => 'order_id',
        'fields' => array(
            'tracking_id' => array('type' => self::TYPE_STRING, 'size' => 255),
            'site_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'form_type_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'form_type' => array('type' => self::TYPE_STRING),
            'payment_method' => array('type' => self::TYPE_STRING),
            'currency_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'currency_to_usd_rate' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat', 'required' => false),
            'gmt' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'customer_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'writer_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'requested_writer_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'editor_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'status_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'parent_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'service_type' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'tariff_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'title' => array('type' => self::TYPE_STRING),
            'academic_level_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'paper_type_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'paper_type_option' => array('type' => self::TYPE_STRING),
            'topic_category_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'topic_category_option' => array('type' => self::TYPE_STRING),
            'is_complex_assignment' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'paper_format_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'paper_format_option' => array('type' => self::TYPE_STRING),
            'writer_category_id' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'writer_percent' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'pages' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'charts' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'slides' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'sources' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'spacing' => array('type' => self::TYPE_STRING),
            'paper_details' => array('type' => self::TYPE_STRING, 'validate' => 'isString'),
            'comments' => array('type' => self::TYPE_STRING),
            'writer_samples' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'used_sources' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'expert_proofreading' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'vip_support' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'plagiarism_report' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'draft_outline' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'progressive_delivery' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'raw_cost' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'total' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'tax' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'discount' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'discount_type' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'price_per_page' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'total_paid' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'paid_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'is_paid' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'writer_pay' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'is_writer_paid' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'writer_paid_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'is_writer_confirmed' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'editor_pay' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
            'is_editor_paid' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'editor_paid_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'is_editor_confirmed' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'hrs_customer' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'customer_deadline' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'hrs_writer' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
            'writer_deadline' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'writer_assigned' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'writer_assigned_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'editor_assigned' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'editor_assigned_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'approved_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'completed_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'cancelled_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'delivered_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'finished_at' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'),
            'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
        )
    );
    
    /**
     * constructor.
     *
     * @param null $id
     */
    public function __construct($id = null)
    {
        parent::__construct($id);

        if($this->id) {
            // currency 
            $default_currency_id = Configuration::get('CURRENCY_DEFAULT', $this->site_id, 1);

            $currency_id = $this->currency_id ? $this->currency_id : $default_currency_id;
            $currency = new Currency((int) $currency_id);
            $currency->conversion_rate = $this->currency_to_usd_rate ? (float) $this->currency_to_usd_rate : (float) $currency->conversion_rate;
            $this->currency = $currency;
        }
    }

    public function getObject( $order = array() ) {
        if( !ArrayUtils::has($order, 'order_id') ) {
            return $this;
        }

        $this->id = $order['order_id'];

        foreach ($order as $key => $value) {
            if (property_exists($this, $key)) {
                $this->{$key} = $value;
            }
        }
        
        return $this;
    }

    public function add($autodate = true, $null_values = true) {
		if ( !$this->getNextOrderId() ) {
			$this->id = self::START_ORDER_ID;
		} else {
            $order_autoincrement = Configuration::get('ORDER_AUTOINCREMENT', PROX_SITE_ID, 2);
			$next_order_id = Db::getInstance()->getValue(
                'SELECT MAX(`order_id`)+' . (int) $order_autoincrement . ' FROM ' . Db::prefix('order')
            );
			$this->id = $next_order_id;
        }
        
        $this->force_id = true;
        
        if(!$this->currency_id) {
            $currency = new Currency( (int) $this->currency_id );
            if(Validate::isLoadedObject($currency)) {
                $this->currency_id = $currency->id;
                $this->currency_to_usd_rate = $currency->conversion_rate;
            }
        }

		return parent::add($autodate, $null_values);
    }

    public function delete() {
        $success = parent::delete();

        if($success) {
            Db::getInstance()->delete('additional_service', 'order_id = ' . (int) $this->id );
            Db::getInstance()->delete('paypal_payment', 'node_id = ' . (int) $this->id );
            Db::getInstance()->delete('order_bid', 'order_id = ' . (int) $this->id );
            Db::getInstance()->delete('order_view', 'order_id = ' . (int) $this->id );
            Db::getInstance()->delete('order_file', 'order_id = ' . (int) $this->id );
            Db::getInstance()->delete('order_message', 'order_id = ' . (int) $this->id );
            Db::getInstance()->delete('order_revision', 'order_id = ' . (int) $this->id );
            Db::getInstance()->delete('order_meta', 'order_id = ' . (int) $this->id );
            Db::getInstance()->delete('order_rating', 'order_id = ' . (int) $this->id );
            Db::getInstance()->delete('notification', 'action = "order" AND node_url = ' . (int) $this->id );
        }
    }

    public function getItemTitle($id) {
        switch ($id) {
            case 2: 
                return 'Best available';
                break;

            case 3: 
                return 'Advanced';
                break;

            case 4: 
                return 'ENL';
                break;

            default: 
                return $id;
                break;
        }
    }

    public function getItemTitleUk($id, $academicLevel) {
        switch ($academicLevel) {
            case LEVEL_HIGHSCHOOL:
              switch ($id) {
                case 2: 
                    return 'Grade B';
                    break;

                case 3: 
                    return 'Grade A';
                    break;

                case 4: 
                    return 'Grade A+';
                    break;

                default: 
                    return $this->getItemTitle($id);
                    break;
            }

            case LEVEL_COLLEGE:
            case LEVEL_UNIVERSITY:
                switch ($id) {
                    case 2: 
                        return 'Undergraduate 2:2';
                        break;

                    case 3: 
                        return 'Undergraduate 2:1';
                        break;

                    case 4: 
                        return 'Undergraduate 1st';
                        break;

                    default: 
                        return  $this->getItemTitle($id);
                        break;
                }
            case LEVEL_MASTERS:
              switch ($id) {
                case 2: 
                    return 'Pass';
                    break;
                case 3: 
                    return 'Merit';
                    break;
                case 4: 
                    return 'Distinction';
                    break;
                default: 
                    return $this->getItemTitle($id);
                    break;
              }
            default: 
                return $this->getItemTitle($id);
          }
    }

    public function present( $fullInfo = true ) {
        global $globals; 
        $app = Application::getInstance();
        $user = $app->container->user;
        
        if( !is_int($this->service_type) ) {
            $this->service_type = self::COMPLEX_ASSIGNMENT;
        }

        $paperType = new PaperType( (int) $this->paper_type_id);
        $paperFormat = new PaperFormat( (int) $this->paper_format_id);

        $academicLevel = ArrayUtils::get($globals['academicLevels'], (int) $this->academic_level_id);
        $status = ArrayUtils::get($globals['orderStatuses'], (int) $this->status_id);
        $service_type = ArrayUtils::get($globals['serviceTypes'], (int) $this->service_type);
        
        $discipline = new Discipline( (int) $this->topic_category_id );

        // init variables
        $canSubmitOrder = false;
        $count_order_files = $count_unseen_files = $count_order_messages = $count_unread_messages = 0;
        $pdSchedule = $messages = $additionalPayments = $additionalServices = $customer = $writer = $editor = $order_history = $writer_files = $customer_files = array();
        $customer_files_count = $writer_files_count = 0;

        // order site
        $site = new Site( (int) $this->site_id );
    
        /** Order files */
        $orderFiles = $this->getFiles();
        $filePresenter = new OrderFilePresenter();

        foreach ($orderFiles as $file) {
            $file = new File( (int) $file['file_id']); 
            if( Validate::isLoadedObject($file) ) {
                if ( $file->uploader_id == $this->customer_id ) {
                    $customer_files[] = $filePresenter->present($file);
                    $customer_files_count++;
                } else {
                    $writer_files[] = $filePresenter->present($file);
                    $writer_files_count++;
                }
            } 
        }

        if( $writer_files_count > 0 ) {
            $canSubmitOrder = true;
        }

        $count_order_files = $writer_files_count + $customer_files_count;

        /** Order Messages */
        $orderMessages = $this->getMessages();
        foreach( $orderMessages as $message ) {
            $messages[] = $message->present();
            if ($message->sender_id != $this->customer_id && !$message->is_viewed) {
                $count_unread_messages++;
            }
        }

        $count_order_messages = count($orderMessages);

        /** Additional Payments */
        $additionalPayments = $this->getAdditionalPayments();

        /** Additional Services **/
        $additionalServices = $this->getAdditionalServices();

        $customer = $this->getCustomer();
        $writer = $this->getWriter();
        $editor = $this->getEditor();

        $customer_deadline = DateUtils::convertToISOFormat($this->customer_deadline);
        $writer_deadline = Validate::isDate($this->writer_deadline) ? DateUtils::convertToISOFormat($this->writer_deadline) : DateUtils::convertToISOFormat($this->customer_deadline);
        $deadline = $user->is_writer && Validate::isDate($this->writer_deadline) ? $writer_deadline : $customer_deadline;

        $writer_pay = $this->writer_pay;
        $editor_pay = $this->editor_pay;

        // use custom CPP only when the employee is viewing and not assigned
        if(
            $user->is_writer && 
            $this->writer_id !== $user->id
        ) {
            if(
                $user->cpp_type == "custom" &&
                $user->cpp_amount > 0
            ) {
                Configuration::set('WRITER_CPP_TYPE', "fixed", PROX_SITE_ID);
                Configuration::set('WRITER_AMOUNT_CPP', $user->cpp_amount , PROX_SITE_ID);

                $calc = $this->calculate();
                $writer_pay = (float) ArrayUtils::get($calc, 'writerCost');
            }
        }

        if(
            $user->is_editor && 
            $this->editor_id !== $user->id
        ) {
            if(
                $user->cpp_type == "custom" &&
                $user->cpp_amount > 0
            ) {
                Configuration::set('EDITOR_CPP_TYPE', "fixed", PROX_SITE_ID);
                Configuration::set('EDITOR_AMOUNT_CPP', $user->cpp_amount , PROX_SITE_ID);

                $calc = $this->calculate();
                $writer_pay = (float) ArrayUtils::get($calc, 'writerCost');
            }
        }

        if($this->requested_writer_id) {
            $requestedWriter = new Employee( (int) $this->requested_writer_id );
        } else {
            $requestedWriter = new Employee();
        }

        $objectPresenter = new ObjectPresenter();

        // $paper_details = Tools::getStringClean($this->paper_details);

        $ukForm = Configuration::get('UK_FORM', $this->site_id, false);

        $order = array(
            'id' => (int) $this->id,
            'parentId' => (int) $this->parent_id,
            'trackingId' => (string) $this->tracking_id,
            'ukForm' => (bool) $ukForm,
            'customer' => $objectPresenter->present($customer),
            'writer' => $objectPresenter->present($writer),
            'requestedWriter' => $objectPresenter->present($requestedWriter),
            'editor' => $objectPresenter->present($editor),
            'site' => $objectPresenter->present($site),
            'serviceType' => (int) $this->service_type,
            'serviceTypeTitle' => ArrayUtils::get($service_type, 'title'),
            'status' => (int) $this->status_id,
            'statusId' => (int) $this->status_id,
			'statusTitle' => ArrayUtils::get($status, 'title'),
            'statusDescription' => ArrayUtils::get($status, 'description'),
            'writerCategoryId' => $this->writer_category_id,
			'writerCategoryTitle' => $ukForm ? $this->getItemTitleUk($this->writer_category_id, $this->academic_level_id): $this->getItemTitle($this->writer_category_id),
            'title' => $this->title,
            'titleTruncated' => Tools::truncateString($this->title, 35),
			'academicLevel' => (int) $this->academic_level_id,
			'academicLevelTitle' => ArrayUtils::get($academicLevel, 'title'),
            'paperType' => (int) $this->paper_type_id,
			'paperTypeTitle' => $paperType->title,
			'paperTypeOption' => $this->paper_type_option,
			'topcatId' => (int) $this->topic_category_id,
            'topcat' => $discipline->title,
			'topcatOption' => $this->topic_category_option,
			'paperFormat' => (int) $this->paper_format_id,
			'paperFormatTitle' => $paperFormat->title,
			'paperFormatTypeOption' => $this->paper_format_option,
            'pages' => (int) $this->pages,
			'slides' => (int) $this->slides,
			'charts' => (int) $this->charts,
			'sources' => (int) $this->sources,
            'spacing' => $this->spacing,
			'paperDetailsRaw' => $this->paper_details,
			'paperDetails' => Tools::nl2br($this->paper_details),
            'comments' => $this->comments,
            'paymentMethod' => $this->payment_method,
            'tariffId' => (int) $this->tariff_id,
			'tariffHrs' => (int) $this->hrs_customer,
			'tariffPricePerPage' => (float) $this->price_per_page,
            'price' => (float) $this->total,
            'noDiscountPrice' => (float) $this->raw_cost,
            'couponType' => (int) $this->discount_type,
            'couponValue' => (int) $this->discount,
            'tax' => (float) $this->tax,
            'paid' => $this->is_paid ? 1 : 0,
            'writerPay' => (float) $writer_pay,
            'isWriterPaid' => $this->is_writer_paid ? 1 : 0,
            'writerAssigned' => $this->writer_assigned,
            'writerAssignedAt' => $this->writer_assigned_at,
            'writerConfirmed' => $this->is_writer_confirmed ? 1 : 0,
            'editorPay' => (float) $editor_pay,
            'isEditorPaid' => $this->is_editor_paid ? 1 : 0,
            'editorAssigned' => $this->editor_assigned,
            'editorAssignedAt' => $this->editor_assigned_at,
            'editorConfirmed' => $this->is_editor_confirmed ? 1 : 0,
            'progressiveDeliveryHidden' => (bool) $this->progressive_delivery ? 1 : 0,
			'areSamplesRequired' => $this->writer_samples ? 1 : 0,
			'areSourcesRequired' => $this->used_sources ? 1 : 0,
            'expertProofreading' => $this->expert_proofreading,
            'vipSupport' => (bool) $this->vip_support,
            'plagiarismReport' => (bool) $this->plagiarism_report,
            'draftOutline' => (bool) $this->draft_outline,
            'services' => $additionalServices,
            'additionalPayments' => $additionalPayments,
            'allFilesCount' => (int) $count_order_files,
            'newFilesCount' => (int) $count_unseen_files,
            'allMessagesCount' => (int) $count_order_messages,
            'newMessagesCount' => (int) $count_unread_messages,
            'messages' => $messages,
            'isOnTimeQuestionNeeded' => (bool) true,
            'hasPDSchedule' => count($pdSchedule) > 0 ? true : false,
            'isPdScheduleAccepted' => (bool) false,
            'isPdScheduleRejected' => (bool) false,
            'showScheduleApproveRejectButton' => (bool) true,
            'pdSchedule' => $pdSchedule,
            'customerFiles' => $customer_files,
            'writerFiles' => $writer_files,
            'withAdditionalMaterials' => count($customer_files) > 0 ? true : false,
            'canSubmitOrder' => (bool) $canSubmitOrder,
            'has_reassignments' => false,
            'fullRefundAllowed' => (bool) false,
            'deadline' => $deadline,
            'realDeadline' => $customer_deadline,
            'writerDeadline' => $writer_deadline,
            'timeLeft' => $deadline ? DateUtils::timeLeft( $deadline ) : 'N/A',
            'timeLeftHtml' => DateUtils::timeLeft( $deadline, false, true ),
			'writerTimeLeftHtml' => $this->is_paid ? DateUtils::timeLeft( $writer_deadline, false, true ) : null,
            'completedAt' => $this->completed_at ? DateUtils::convertToISOFormat($this->completed_at) : null,
            'finishedAt' => $this->delivered_at ? DateUtils::convertToISOFormat($this->delivered_at) : null,
            'createdAt' => DateUtils::convertToISOFormat($this->date_add),
            'approvedAt' => $this->status_id == self::FINISHED ? DateUtils::convertToISOFormat($this->approved_at) : null,
            'cancelledDate' => $this->status_id == self::CANCELLED ? DateUtils::convertToISOFormat($this->cancelled_at) : null,
        ); 

        return $order;
    }

    public function calculate() {
        if( $this->service_type == self::COMPLEX_ASSIGNMENT ) {
            $priceCalculator = new PriceCalculator();
            $defaultOrder = $this->defaultOrder();

            return $priceCalculator->calculate( $defaultOrder );
        } else {
            $pagesPriceMultiplicator = $this->spacing == "single" ? 2 : 1;
            $isComplexAssigment = $this->is_complex_assignment;
            $price = 0;

            $price = ($this->pages * $pagesPriceMultiplicator * $this->price_per_page) +
                     ($this->slides / 2) * $this->price_per_page +
                     ($this->charts / 2) * $this->price_per_page;

            $complexCost = 0;
            if ($isComplexAssigment) {
                $complexCost = ($price / 100) * 20;
            }

            $writerCost = 0;
            if($this->requested_writer_id) {
                $writerCost = ($price / 100) * $this->writer_percent;
            }
                
            $discountPrice = 0;
            if( $this->discount ) {
                if( $this->discount_type == Coupon::PERCENT ) {
                    $discountPrice = ($price * $this->discount) / 100;
                } else {
                    $discountPrice = $this->discount;
                }
            }

            $price += $complexCost + $writerCost;
            $totalCost = $price - $discountPrice;

            $pricesCost = array(
                'rawCost' => $price,
                'totalCost' => $totalCost
            );

            return $pricesCost;
        }
        
    }

    public function defaultOrder() {

        $discount = null;
        if($this->discount) {
            $discount = array(
                'type_id' => $this->discount_type,
                'value' => $this->discount
            );
        }

        $defaultOrder = array (
            'pages' =>                              (int) $this->pages,
            'slides' =>                             (int) $this->slides,
            'discount' =>                           $discount,
            'charts' =>                             (int) $this->charts,
            'winbackCoupons' =>                     $this->winbackCoupons,
            'writerCategoryId' =>                   $this->writer_category_id,
            'tariffPricePerPage' =>                 $this->price_per_page,
            'tariffHrs' =>                          $this->hrs_customer,
            'spacing' =>                            $this->spacing,
            'getSamplesOn' =>                       $this->writer_samples,
            'getProgressiveDeliveryOn' =>           $this->progressive_delivery,
            'getUsedSourcesOn' =>                   $this->used_sources,
            'complexAssignmentDiscipline' =>        (bool) $this->is_complex_assignment,
            'writerPercent' =>                      (int) $this->writer_percent,
            'expertProofreading' =>                 (bool) $this->expert_proofreading,
            'vipSupport' =>                         (bool) $this->vip_support,
            'plagiarismReport' =>                   (bool) $this->plagiarism_report,
            'draftOutline' =>                       (bool) $this->draft_outline
        );

        return $defaultOrder;
    }

    /**
     * used to cache additional payments.
     */
    protected $additionalServices = array();

    public function getAdditionalServices() {
        $options = array();
        $currency = $this->currency;

        if(Validate::isLoadedObject($currency)) {
            $options['currency'] = array(
                'symbol' => $currency->symbol,
                'rate' => (float) $this->currency_to_usd_rate ? (float) $this->currency_to_usd_rate : (float) $currency->conversion_rate
            );
        }
        
        if (empty($this->additionalServices)) {
            $calculatedCosts = $this->calculate();
            $servicesById = ArrayUtils::get($calculatedCosts, 'servicesById');

            $additionalServices = array();

            if($this->writer_samples) {
                $writerSamplesCost = ArrayUtils::get($servicesById, self::PROVIDE_ME_SAMPLES);
                $writerSamplesCost_price = (float) ArrayUtils::get($writerSamplesCost, 'price');

                $additionalServices[] = array(
                    "title" => "Order writer’s samples", 
                    "typeId" => self::PROVIDE_ME_SAMPLES, 
                    "coupon_data" => null, 
                    "description" => formatPrice($writerSamplesCost_price, $options), 
                );
            }

            if($this->used_sources) {
                $usedSourcesCost = ArrayUtils::get($servicesById, self::USED_SOURCES);
                $usedSourcesCost_price = (float) ArrayUtils::get($usedSourcesCost, 'price');

                $additionalServices[] = array(
                    "title" => "Copy of sources used", 
                    "typeId" => self::USED_SOURCES, 
                    "coupon_data" => null, 
                    "description" => formatPrice($usedSourcesCost_price, $options),
                );
            }


            if($this->expert_proofreading) {
                $expertProofreadingCost = ArrayUtils::get($servicesById, self::EXPERT_PROOFREADING);
                $expertProofreadingCost_price = (float) ArrayUtils::get($expertProofreadingCost, 'price');

                $additionalServices[] = array(
                    "title" => "Expert Proofreading", 
                    "typeId" => self::EXPERT_PROOFREADING, 
                    "coupon_data" => null, 
                    "description" => formatPrice($expertProofreadingCost_price, $options)
                );
            }

            if($this->vip_support) {
                $vipSupportCost = ArrayUtils::get($servicesById, self::VIP_SUPPORT);
                $vipSupportCost_price = (float) ArrayUtils::get($vipSupportCost, 'price');

                $additionalServices[] = array(
                    "title" => "VIP Support", 
                    "typeId" => self::VIP_SUPPORT, 
                    "coupon_data" => null, 
                    "description" => formatPrice($vipSupportCost_price, $options)
                );
            }

            if($this->plagiarism_report) {
                $plagiarismReportCost = ArrayUtils::get($servicesById, self::PLAGIARISM_REPORT);
                $plagiarismReportCost_price = (float) ArrayUtils::get($plagiarismReportCost, 'price');

                $additionalServices[] = array(
                    "title" => "Plagiarism Report", 
                    "typeId" => self::PLAGIARISM_REPORT, 
                    "coupon_data" => null, 
                    "description" => formatPrice($plagiarismReportCost_price, $options)
                );
            }

            if($this->draft_outline) {
                $draftOutlineCost = ArrayUtils::get($servicesById, self::DRAFT_OUTLINE);
                $draftOutlineCost_price = (float) ArrayUtils::get($draftOutlineCost, 'price');

                $additionalServices[] = array(
                    "title" => "Draft/Outline", 
                    "typeId" => self::DRAFT_OUTLINE, 
                    "coupon_data" => null, 
                    "description" => formatPrice($draftOutlineCost_price, $options)
                );
            }

            if($this->progressive_delivery) {
                $progressiveDeliveryCost = ArrayUtils::get($servicesById, self::PROGRESSIVE_DELIVERY);
                $progressiveDeliveryCost_price = (float) ArrayUtils::get($progressiveDeliveryCost, 'price');

                $additionalServices[] = array(
                    "title" => "Progressive delivery", 
                    "typeId" => self::PROGRESSIVE_DELIVERY, 
                    "coupon_data" => null, 
                    "description" => formatPrice($progressiveDeliveryCost_price, $options) 
                );
            }

            $this->additionalServices = $additionalServices;
        }

        return $this->additionalServices;
    }

    /**
     * used to cache additional payments.
     */
    protected $additionalPayments = array();

    public function getAdditionalPayments() {
        if (empty($this->additionalPayments)) {

            $additionalPayments = array();

            $sql = new DbQuery();
	        $sql->select('*');
	        $sql->from('additional_service');
			$sql->where('order_id = ' . (int) $this->id);
	        $sql->where('is_paid = 1');
            $result = Db::getInstance()->executeS($sql);
            
            foreach( $result as $additionalPayment ) {
                $amount =  ArrayUtils::get($additionalPayment, 'total');

                $additionalPayments[] = [
                    'id' => (int) ArrayUtils::get($additionalPayment, 'additional_service_id'),
                    'title' => "Additional Payment of " . formatPrice($amount),
                    'price' => (float) $amount,
                    'paidAt' => ArrayUtils::get($additionalPayment, 'paid_at'),
                ];
            }

            $this->additionalPayments = $additionalPayments;
        }

        return $this->additionalPayments;
    }

    /**
     * used to cache order files.
     */
    protected $orderFiles = null;

    public function getFiles() {
        if (is_null($this->orderFiles)) {
            $sql = new DbQuery();
            $sql->select('f.file_id');
            $sql->from('order_file', 'f');
            $sql->where('f.`order_id` = ' . (int) $this->id );
            $sql->orderBy('f.file_id DESC');

            $result = Db::getInstance(PROX_USE_SQL_SLAVE)->executeS( $sql );

            if (!$result) {
                return array();
            }

            $this->orderFiles = $result;
        }

		return $this->orderFiles;
    }
    
    /**
     * used to cache order messages.
     */
    protected $orderMessages = null;

    public function getMessages() {
        $app = Application::getInstance();
        $user = $app->container->user;

        if (is_null($this->orderMessages)) {
            $sql = new DbQuery();
            $sql->select('m.message_id');
            $sql->from('order_message', 'm');
            $sql->where('m.`order_id` = ' . (int) $this->id );
            
            if(!$user->is_admin && !$user->is_sub_admin) {
                $sql->where(' (m.`from_department` = ' . (int) DEPARTMENT_WRITER . ') OR ( m.`to_department` = ' . (int) DEPARTMENT_WRITER . ' AND is_verified = 1 ) ');
            } 

            $sql->orderBy('m.message_id DESC');

            $result = Db::getInstance(PROX_USE_SQL_SLAVE)->executeS( $sql );

            if (!$result) {
                return array();
            }

            $messages = array();
            foreach( $result as $message ) {
                $message = new Message( (int) $message['message_id'] );
                if(Validate::isLoadedObject($message)) {
                    $messages[] = $message;
                }
            }

            $this->orderMessages = $messages;
        }

		return $this->orderMessages;
    }

    /**
     * used to cache order writer.
     */
    protected $cacheWriter = null;

    /**
     * Get order writer.
     *
     * @return Employee $writer
     */
    public function getWriter()
    {
        if (is_null($this->cacheWriter)) {
            $this->cacheWriter = new Employee((int) $this->writer_id);
        }

        return $this->cacheWriter;
    }
    
    /**
     * used to cache order editor.
     */
    protected $cacheEditor = null;

    /**
     * Get order editor.
     *
     * @return Employee $editor
     */
    public function getEditor()
    {
        if (is_null($this->cacheEditor)) {
            $this->cacheEditor = new Employee((int) $this->editor_id);
        }

        return $this->cacheEditor;
	}

	/**
     * used to cache order customer.
     */
    protected $cacheCustomer = null;

    /**
     * Get order customer.
     *
     * @return User $customer
     */
    public function getCustomer()
    {
        if (is_null($this->cacheCustomer)) {
            $this->cacheCustomer = new Customer((int) $this->customer_id);
        }

        return $this->cacheCustomer;
    }
    
    /**
     * This method return the ID of the previous order.
     *
     * @since 1.5.0.1
     *
     * @return int
     */
    public function getPreviousOrderId( $customerId = null )
    {
        return Db::getInstance()->getValue('
            SELECT order_id
            FROM ' . DB_PREFIX . 'order
			WHERE order_id < ' . (int) $this->id . 
			($customerId ? ' AND customer_id = '. (int) $customerId : '') . '
            ORDER BY order_id DESC');
    }
	
	/**
     * This method return the ID of the next order.
     *
     * @since 1.0.0
     *
     * @return int
     */
    public function getNextOrderId( $customerId = null )
    {
        return Db::getInstance()->getValue('
            SELECT order_id
            FROM ' . DB_PREFIX . 'order
			WHERE order_id > ' . (int) $this->id . 
			($customerId ? ' AND customer_id = '. (int) $customerId : '') . '
            ORDER BY order_id ASC');
    }
}