<?php

namespace PhpOffice\PhpSpreadsheet\Reader;

use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Border;

class Slk extends BaseReader
{
    /**
     * Input encoding.
     *
     * @var string
     */
    private $inputEncoding = 'ANSI';

    /**
     * Sheet index to read.
     *
     * @var int
     */
    private $sheetIndex = 0;

    /**
     * Formats.
     *
     * @var array
     */
    private $formats = [];

    /**
     * Format Count.
     *
     * @var int
     */
    private $format = 0;

    /**
     * Create a new SYLK Reader instance.
     */
    public function __construct()
    {
        $this->readFilter = new DefaultReadFilter();
    }

    /**
     * Validate that the current file is a SYLK file.
     *
     * @param string $pFilename
     *
     * @return bool
     */
    public function canRead($pFilename)
    {
        // Check if file exists
        try {
            $this->openFile($pFilename);
        } catch (Exception $e) {
            return false;
        }

        // Read sample data (first 2 KB will do)
        $data = fread($this->fileHandle, 2048);

        // Count delimiters in file
        $delimiterCount = substr_count($data, ';');
        $hasDelimiter = $delimiterCount > 0;

        // Analyze first line looking for ID; signature
        $lines = explode("\n", $data);
        $hasId = substr($lines[0], 0, 4) === 'ID;P';

        fclose($this->fileHandle);

        return $hasDelimiter && $hasId;
    }

    /**
     * Set input encoding.
     *
     * @param string $pValue Input encoding, eg: 'ANSI'
     *
     * @return Slk
     */
    public function setInputEncoding($pValue)
    {
        $this->inputEncoding = $pValue;

        return $this;
    }

    /**
     * Get input encoding.
     *
     * @return string
     */
    public function getInputEncoding()
    {
        return $this->inputEncoding;
    }

    /**
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
     *
     * @param string $pFilename
     *
     * @throws Exception
     *
     * @return array
     */
    public function listWorksheetInfo($pFilename)
    {
        // Open file
        if (!$this->canRead($pFilename)) {
            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
        }
        $this->openFile($pFilename);
        $fileHandle = $this->fileHandle;
        rewind($fileHandle);

        $worksheetInfo = [];
        $worksheetInfo[0]['worksheetName'] = 'Worksheet';
        $worksheetInfo[0]['lastColumnLetter'] = 'A';
        $worksheetInfo[0]['lastColumnIndex'] = 0;
        $worksheetInfo[0]['totalRows'] = 0;
        $worksheetInfo[0]['totalColumns'] = 0;

        // loop through one row (line) at a time in the file
        $rowIndex = 0;
        while (($rowData = fgets($fileHandle)) !== false) {
            $columnIndex = 0;

            // convert SYLK encoded $rowData to UTF-8
            $rowData = StringHelper::SYLKtoUTF8($rowData);

            // explode each row at semicolons while taking into account that literal semicolon (;)
            // is escaped like this (;;)
            $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData)))));

            $dataType = array_shift($rowData);
            if ($dataType == 'C') {
                //  Read cell value data
                foreach ($rowData as $rowDatum) {
                    switch ($rowDatum[0]) {
                        case 'C':
                        case 'X':
                            $columnIndex = substr($rowDatum, 1) - 1;

                            break;
                        case 'R':
                        case 'Y':
                            $rowIndex = substr($rowDatum, 1);

                            break;
                    }

                    $worksheetInfo[0]['totalRows'] = max($worksheetInfo[0]['totalRows'], $rowIndex);
                    $worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], $columnIndex);
                }
            }
        }

        $worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1);
        $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;

        // Close file
        fclose($fileHandle);

        return $worksheetInfo;
    }

    /**
     * Loads PhpSpreadsheet from file.
     *
     * @param string $pFilename
     *
     * @throws Exception
     *
     * @return Spreadsheet
     */
    public function load($pFilename)
    {
        // Create new Spreadsheet
        $spreadsheet = new Spreadsheet();

        // Load into this instance
        return $this->loadIntoExisting($pFilename, $spreadsheet);
    }

    /**
     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
     *
     * @param string $pFilename
     * @param Spreadsheet $spreadsheet
     *
     * @throws Exception
     *
     * @return Spreadsheet
     */
    public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
    {
        // Open file
        if (!$this->canRead($pFilename)) {
            throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
        }
        $this->openFile($pFilename);
        $fileHandle = $this->fileHandle;
        rewind($fileHandle);

        // Create new Worksheets
        while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
            $spreadsheet->createSheet();
        }
        $spreadsheet->setActiveSheetIndex($this->sheetIndex);

        $fromFormats = ['\-', '\ '];
        $toFormats = ['-', ' '];

        // Loop through file
        $column = $row = '';

        // loop through one row (line) at a time in the file
        while (($rowData = fgets($fileHandle)) !== false) {
            // convert SYLK encoded $rowData to UTF-8
            $rowData = StringHelper::SYLKtoUTF8($rowData);

            // explode each row at semicolons while taking into account that literal semicolon (;)
            // is escaped like this (;;)
            $rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData)))));

            $dataType = array_shift($rowData);
            //    Read shared styles
            if ($dataType == 'P') {
                $formatArray = [];
                foreach ($rowData as $rowDatum) {
                    switch ($rowDatum[0]) {
                        case 'P':
                            $formatArray['numberFormat']['formatCode'] = str_replace($fromFormats, $toFormats, substr($rowDatum, 1));

                            break;
                        case 'E':
                        case 'F':
                            $formatArray['font']['name'] = substr($rowDatum, 1);

                            break;
                        case 'L':
                            $formatArray['font']['size'] = substr($rowDatum, 1);

                            break;
                        case 'S':
                            $styleSettings = substr($rowDatum, 1);
                            $iMax = strlen($styleSettings);
                            for ($i = 0; $i < $iMax; ++$i) {
                                switch ($styleSettings[$i]) {
                                    case 'I':
                                        $formatArray['font']['italic'] = true;

                                        break;
                                    case 'D':
                                        $formatArray['font']['bold'] = true;

                                        break;
                                    case 'T':
                                        $formatArray['borders']['top']['borderStyle'] = Border::BORDER_THIN;

                                        break;
                                    case 'B':
                                        $formatArray['borders']['bottom']['borderStyle'] = Border::BORDER_THIN;

                                        break;
                                    case 'L':
                                        $formatArray['borders']['left']['borderStyle'] = Border::BORDER_THIN;

                                        break;
                                    case 'R':
                                        $formatArray['borders']['right']['borderStyle'] = Border::BORDER_THIN;

                                        break;
                                }
                            }

                            break;
                    }
                }
                $this->formats['P' . $this->format++] = $formatArray;
            //    Read cell value data
            } elseif ($dataType == 'C') {
                $hasCalculatedValue = false;
                $cellData = $cellDataFormula = '';
                foreach ($rowData as $rowDatum) {
                    switch ($rowDatum[0]) {
                        case 'C':
                        case 'X':
                            $column = substr($rowDatum, 1);

                            break;
                        case 'R':
                        case 'Y':
                            $row = substr($rowDatum, 1);

                            break;
                        case 'K':
                            $cellData = substr($rowDatum, 1);

                            break;
                        case 'E':
                            $cellDataFormula = '=' . substr($rowDatum, 1);
                            //    Convert R1C1 style references to A1 style references (but only when not quoted)
                            $temp = explode('"', $cellDataFormula);
                            $key = false;
                            foreach ($temp as &$value) {
                                //    Only count/replace in alternate array entries
                                if ($key = !$key) {
                                    preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
                                    //    Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
                                    //        through the formula from left to right. Reversing means that we work right to left.through
                                    //        the formula
                                    $cellReferences = array_reverse($cellReferences);
                                    //    Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
                                    //        then modify the formula to use that new reference
                                    foreach ($cellReferences as $cellReference) {
                                        $rowReference = $cellReference[2][0];
                                        //    Empty R reference is the current row
                                        if ($rowReference == '') {
                                            $rowReference = $row;
                                        }
                                        //    Bracketed R references are relative to the current row
                                        if ($rowReference[0] == '[') {
                                            $rowReference = $row + trim($rowReference, '[]');
                                        }
                                        $columnReference = $cellReference[4][0];
                                        //    Empty C reference is the current column
                                        if ($columnReference == '') {
                                            $columnReference = $column;
                                        }
                                        //    Bracketed C references are relative to the current column
                                        if ($columnReference[0] == '[') {
                                            $columnReference = $column + trim($columnReference, '[]');
                                        }
                                        $A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference;

                                        $value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0]));
                                    }
                                }
                            }
                            unset($value);
                            //    Then rebuild the formula string
                            $cellDataFormula = implode('"', $temp);
                            $hasCalculatedValue = true;

                            break;
                    }
                }
                $columnLetter = Coordinate::stringFromColumnIndex($column);
                $cellData = Calculation::unwrapResult($cellData);

                // Set cell value
                $spreadsheet->getActiveSheet()->getCell($columnLetter . $row)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData);
                if ($hasCalculatedValue) {
                    $cellData = Calculation::unwrapResult($cellData);
                    $spreadsheet->getActiveSheet()->getCell($columnLetter . $row)->setCalculatedValue($cellData);
                }
                //    Read cell formatting
            } elseif ($dataType == 'F') {
                $formatStyle = $columnWidth = $styleSettings = '';
                $styleData = [];
                foreach ($rowData as $rowDatum) {
                    switch ($rowDatum[0]) {
                        case 'C':
                        case 'X':
                            $column = substr($rowDatum, 1);

                            break;
                        case 'R':
                        case 'Y':
                            $row = substr($rowDatum, 1);

                            break;
                        case 'P':
                            $formatStyle = $rowDatum;

                            break;
                        case 'W':
                            list($startCol, $endCol, $columnWidth) = explode(' ', substr($rowDatum, 1));

                            break;
                        case 'S':
                            $styleSettings = substr($rowDatum, 1);
                            $iMax = strlen($styleSettings);
                            for ($i = 0; $i < $iMax; ++$i) {
                                switch ($styleSettings[$i]) {
                                    case 'I':
                                        $styleData['font']['italic'] = true;

                                        break;
                                    case 'D':
                                        $styleData['font']['bold'] = true;

                                        break;
                                    case 'T':
                                        $styleData['borders']['top']['borderStyle'] = Border::BORDER_THIN;

                                        break;
                                    case 'B':
                                        $styleData['borders']['bottom']['borderStyle'] = Border::BORDER_THIN;

                                        break;
                                    case 'L':
                                        $styleData['borders']['left']['borderStyle'] = Border::BORDER_THIN;

                                        break;
                                    case 'R':
                                        $styleData['borders']['right']['borderStyle'] = Border::BORDER_THIN;

                                        break;
                                }
                            }

                            break;
                    }
                }
                if (($formatStyle > '') && ($column > '') && ($row > '')) {
                    $columnLetter = Coordinate::stringFromColumnIndex($column);
                    if (isset($this->formats[$formatStyle])) {
                        $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->formats[$formatStyle]);
                    }
                }
                if ((!empty($styleData)) && ($column > '') && ($row > '')) {
                    $columnLetter = Coordinate::stringFromColumnIndex($column);
                    $spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($styleData);
                }
                if ($columnWidth > '') {
                    if ($startCol == $endCol) {
                        $startCol = Coordinate::stringFromColumnIndex($startCol);
                        $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth);
                    } else {
                        $startCol = Coordinate::stringFromColumnIndex($startCol);
                        $endCol = Coordinate::stringFromColumnIndex($endCol);
                        $spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth);
                        do {
                            $spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth($columnWidth);
                        } while ($startCol != $endCol);
                    }
                }
            } else {
                foreach ($rowData as $rowDatum) {
                    switch ($rowDatum[0]) {
                        case 'C':
                        case 'X':
                            $column = substr($rowDatum, 1);

                            break;
                        case 'R':
                        case 'Y':
                            $row = substr($rowDatum, 1);

                            break;
                    }
                }
            }
        }

        // Close file
        fclose($fileHandle);

        // Return
        return $spreadsheet;
    }

    /**
     * Get sheet index.
     *
     * @return int
     */
    public function getSheetIndex()
    {
        return $this->sheetIndex;
    }

    /**
     * Set sheet index.
     *
     * @param int $pValue Sheet index
     *
     * @return Slk
     */
    public function setSheetIndex($pValue)
    {
        $this->sheetIndex = $pValue;

        return $this;
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Writer\Xls;

use PhpOffice\PhpSpreadsheet\Shared\StringHelper;

class Font
{
    /**
     * Color index.
     *
     * @var int
     */
    private $colorIndex;

    /**
     * Font.
     *
     * @var \PhpOffice\PhpSpreadsheet\Style\Font
     */
    private $font;

    /**
     * Constructor.
     *
     * @param \PhpOffice\PhpSpreadsheet\Style\Font $font
     */
    public function __construct(\PhpOffice\PhpSpreadsheet\Style\Font $font)
    {
        $this->colorIndex = 0x7FFF;
        $this->font = $font;
    }

    /**
     * Set the color index.
     *
     * @param int $colorIndex
     */
    public function setColorIndex($colorIndex)
    {
        $this->colorIndex = $colorIndex;
    }

    /**
     * Get font record data.
     *
     * @return string
     */
    public function writeFont()
    {
        $font_outline = 0;
        $font_shadow = 0;

        $icv = $this->colorIndex; // Index to color palette
        if ($this->font->getSuperscript()) {
            $sss = 1;
        } elseif ($this->font->getSubscript()) {
            $sss = 2;
        } else {
            $sss = 0;
        }
        $bFamily = 0; // Font family
        $bCharSet = \PhpOffice\PhpSpreadsheet\Shared\Font::getCharsetFromFontName($this->font->getName()); // Character set

        $record = 0x31; // Record identifier
        $reserved = 0x00; // Reserved
        $grbit = 0x00; // Font attributes
        if ($this->font->getItalic()) {
            $grbit |= 0x02;
        }
        if ($this->font->getStrikethrough()) {
            $grbit |= 0x08;
        }
        if ($font_outline) {
            $grbit |= 0x10;
        }
        if ($font_shadow) {
            $grbit |= 0x20;
        }

        $data = pack(
            'vvvvvCCCC',
            // Fontsize (in twips)
            $this->font->getSize() * 20,
            $grbit,
            // Colour
            $icv,
            // Font weight
            self::mapBold($this->font->getBold()),
            // Superscript/Subscript
            $sss,
            self::mapUnderline($this->font->getUnderline()),
            $bFamily,
            $bCharSet,
            $reserved
        );
        $data .= StringHelper::UTF8toBIFF8UnicodeShort($this->font->getName());

        $length = strlen($data);
        $header = pack('vv', $record, $length);

        return $header . $data;
    }

    /**
     * Map to BIFF5-BIFF8 codes for bold.
     *
     * @param bool $bold
     *
     * @return int
     */
    private static function mapBold($bold)
    {
        if ($bold) {
            return 0x2BC; //  700 = Bold font weight
        }

        return 0x190; //  400 = Normal font weight
    }

    /**
     * Map of BIFF2-BIFF8 codes for underline styles.
     *
     * @var array of int
     */
    private static $mapUnderline = [
        \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_NONE => 0x00,
        \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE => 0x01,
        \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLE => 0x02,
        \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLEACCOUNTING => 0x21,
        \PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_DOUBLEACCOUNTING => 0x22,
    ];

    /**
     * Map underline.
     *
     * @param string $underline
     *
     * @return int
     */
    private static function mapUnderline($underline)
    {
        if (isset(self::$mapUnderline[$underline])) {
            return self::$mapUnderline[$underline];
        }

        return 0x00;
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Style;

use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;

class Color extends Supervisor
{
    // Colors
    const COLOR_BLACK = 'FF000000';
    const COLOR_WHITE = 'FFFFFFFF';
    const COLOR_RED = 'FFFF0000';
    const COLOR_DARKRED = 'FF800000';
    const COLOR_BLUE = 'FF0000FF';
    const COLOR_DARKBLUE = 'FF000080';
    const COLOR_GREEN = 'FF00FF00';
    const COLOR_DARKGREEN = 'FF008000';
    const COLOR_YELLOW = 'FFFFFF00';
    const COLOR_DARKYELLOW = 'FF808000';

    /**
     * Indexed colors array.
     *
     * @var array
     */
    protected static $indexedColors;

    /**
     * ARGB - Alpha RGB.
     *
     * @var string
     */
    protected $argb;

    /**
     * Create a new Color.
     *
     * @param string $pARGB ARGB value for the colour
     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
     *                                    Leave this value at default unless you understand exactly what
     *                                        its ramifications are
     * @param bool $isConditional Flag indicating if this is a conditional style or not
     *                                    Leave this value at default unless you understand exactly what
     *                                        its ramifications are
     */
    public function __construct($pARGB = self::COLOR_BLACK, $isSupervisor = false, $isConditional = false)
    {
        //    Supervisor?
        parent::__construct($isSupervisor);

        //    Initialise values
        if (!$isConditional) {
            $this->argb = $pARGB;
        }
    }

    /**
     * Get the shared style component for the currently active cell in currently active sheet.
     * Only used for style supervisor.
     *
     * @return Color
     */
    public function getSharedComponent()
    {
        switch ($this->parentPropertyName) {
            case 'endColor':
                return $this->parent->getSharedComponent()->getEndColor();
            case 'color':
                return $this->parent->getSharedComponent()->getColor();
            case 'startColor':
                return $this->parent->getSharedComponent()->getStartColor();
        }
    }

    /**
     * Build style array from subcomponents.
     *
     * @param array $array
     *
     * @return array
     */
    public function getStyleArray($array)
    {
        return $this->parent->getStyleArray([$this->parentPropertyName => $array]);
    }

    /**
     * Apply styles from array.
     *
     * <code>
     * $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->getColor()->applyFromArray(['rgb' => '808080']);
     * </code>
     *
     * @param array $pStyles Array containing style information
     *
     * @throws PhpSpreadsheetException
     *
     * @return Color
     */
    public function applyFromArray(array $pStyles)
    {
        if ($this->isSupervisor) {
            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
        } else {
            if (isset($pStyles['rgb'])) {
                $this->setRGB($pStyles['rgb']);
            }
            if (isset($pStyles['argb'])) {
                $this->setARGB($pStyles['argb']);
            }
        }

        return $this;
    }

    /**
     * Get ARGB.
     *
     * @return string
     */
    public function getARGB()
    {
        if ($this->isSupervisor) {
            return $this->getSharedComponent()->getARGB();
        }

        return $this->argb;
    }

    /**
     * Set ARGB.
     *
     * @param string $pValue see self::COLOR_*
     *
     * @return Color
     */
    public function setARGB($pValue)
    {
        if ($pValue == '') {
            $pValue = self::COLOR_BLACK;
        }
        if ($this->isSupervisor) {
            $styleArray = $this->getStyleArray(['argb' => $pValue]);
            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
        } else {
            $this->argb = $pValue;
        }

        return $this;
    }

    /**
     * Get RGB.
     *
     * @return string
     */
    public function getRGB()
    {
        if ($this->isSupervisor) {
            return $this->getSharedComponent()->getRGB();
        }

        return substr($this->argb, 2);
    }

    /**
     * Set RGB.
     *
     * @param string $pValue RGB value
     *
     * @return Color
     */
    public function setRGB($pValue)
    {
        if ($pValue == '') {
            $pValue = '000000';
        }
        if ($this->isSupervisor) {
            $styleArray = $this->getStyleArray(['argb' => 'FF' . $pValue]);
            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
        } else {
            $this->argb = 'FF' . $pValue;
        }

        return $this;
    }

    /**
     * Get a specified colour component of an RGB value.
     *
     * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
     * @param int $offset Position within the RGB value to extract
     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
     *                                    decimal value
     *
     * @return string The extracted colour component
     */
    private static function getColourComponent($RGB, $offset, $hex = true)
    {
        $colour = substr($RGB, $offset, 2);
        if (!$hex) {
            $colour = hexdec($colour);
        }

        return $colour;
    }

    /**
     * Get the red colour component of an RGB value.
     *
     * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
     *                                    decimal value
     *
     * @return string The red colour component
     */
    public static function getRed($RGB, $hex = true)
    {
        return self::getColourComponent($RGB, strlen($RGB) - 6, $hex);
    }

    /**
     * Get the green colour component of an RGB value.
     *
     * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
     *                                    decimal value
     *
     * @return string The green colour component
     */
    public static function getGreen($RGB, $hex = true)
    {
        return self::getColourComponent($RGB, strlen($RGB) - 4, $hex);
    }

    /**
     * Get the blue colour component of an RGB value.
     *
     * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
     *                                    decimal value
     *
     * @return string The blue colour component
     */
    public static function getBlue($RGB, $hex = true)
    {
        return self::getColourComponent($RGB, strlen($RGB) - 2, $hex);
    }

    /**
     * Adjust the brightness of a color.
     *
     * @param string $hex The colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
     * @param float $adjustPercentage The percentage by which to adjust the colour as a float from -1 to 1
     *
     * @return string The adjusted colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
     */
    public static function changeBrightness($hex, $adjustPercentage)
    {
        $rgba = (strlen($hex) == 8);

        $red = self::getRed($hex, false);
        $green = self::getGreen($hex, false);
        $blue = self::getBlue($hex, false);
        if ($adjustPercentage > 0) {
            $red += (255 - $red) * $adjustPercentage;
            $green += (255 - $green) * $adjustPercentage;
            $blue += (255 - $blue) * $adjustPercentage;
        } else {
            $red += $red * $adjustPercentage;
            $green += $green * $adjustPercentage;
            $blue += $blue * $adjustPercentage;
        }

        if ($red < 0) {
            $red = 0;
        } elseif ($red > 255) {
            $red = 255;
        }
        if ($green < 0) {
            $green = 0;
        } elseif ($green > 255) {
            $green = 255;
        }
        if ($blue < 0) {
            $blue = 0;
        } elseif ($blue > 255) {
            $blue = 255;
        }

        $rgb = strtoupper(
            str_pad(dechex($red), 2, '0', 0) .
            str_pad(dechex($green), 2, '0', 0) .
            str_pad(dechex($blue), 2, '0', 0)
        );

        return (($rgba) ? 'FF' : '') . $rgb;
    }

    /**
     * Get indexed color.
     *
     * @param int $pIndex Index entry point into the colour array
     * @param bool $background Flag to indicate whether default background or foreground colour
     *                                            should be returned if the indexed colour doesn't exist
     *
     * @return Color
     */
    public static function indexedColor($pIndex, $background = false)
    {
        // Clean parameter
        $pIndex = (int) $pIndex;

        // Indexed colors
        if (self::$indexedColors === null) {
            self::$indexedColors = [
                1 => 'FF000000', //  System Colour #1 - Black
                2 => 'FFFFFFFF', //  System Colour #2 - White
                3 => 'FFFF0000', //  System Colour #3 - Red
                4 => 'FF00FF00', //  System Colour #4 - Green
                5 => 'FF0000FF', //  System Colour #5 - Blue
                6 => 'FFFFFF00', //  System Colour #6 - Yellow
                7 => 'FFFF00FF', //  System Colour #7- Magenta
                8 => 'FF00FFFF', //  System Colour #8- Cyan
                9 => 'FF800000', //  Standard Colour #9
                10 => 'FF008000', //  Standard Colour #10
                11 => 'FF000080', //  Standard Colour #11
                12 => 'FF808000', //  Standard Colour #12
                13 => 'FF800080', //  Standard Colour #13
                14 => 'FF008080', //  Standard Colour #14
                15 => 'FFC0C0C0', //  Standard Colour #15
                16 => 'FF808080', //  Standard Colour #16
                17 => 'FF9999FF', //  Chart Fill Colour #17
                18 => 'FF993366', //  Chart Fill Colour #18
                19 => 'FFFFFFCC', //  Chart Fill Colour #19
                20 => 'FFCCFFFF', //  Chart Fill Colour #20
                21 => 'FF660066', //  Chart Fill Colour #21
                22 => 'FFFF8080', //  Chart Fill Colour #22
                23 => 'FF0066CC', //  Chart Fill Colour #23
                24 => 'FFCCCCFF', //  Chart Fill Colour #24
                25 => 'FF000080', //  Chart Line Colour #25
                26 => 'FFFF00FF', //  Chart Line Colour #26
                27 => 'FFFFFF00', //  Chart Line Colour #27
                28 => 'FF00FFFF', //  Chart Line Colour #28
                29 => 'FF800080', //  Chart Line Colour #29
                30 => 'FF800000', //  Chart Line Colour #30
                31 => 'FF008080', //  Chart Line Colour #31
                32 => 'FF0000FF', //  Chart Line Colour #32
                33 => 'FF00CCFF', //  Standard Colour #33
                34 => 'FFCCFFFF', //  Standard Colour #34
                35 => 'FFCCFFCC', //  Standard Colour #35
                36 => 'FFFFFF99', //  Standard Colour #36
                37 => 'FF99CCFF', //  Standard Colour #37
                38 => 'FFFF99CC', //  Standard Colour #38
                39 => 'FFCC99FF', //  Standard Colour #39
                40 => 'FFFFCC99', //  Standard Colour #40
                41 => 'FF3366FF', //  Standard Colour #41
                42 => 'FF33CCCC', //  Standard Colour #42
                43 => 'FF99CC00', //  Standard Colour #43
                44 => 'FFFFCC00', //  Standard Colour #44
                45 => 'FFFF9900', //  Standard Colour #45
                46 => 'FFFF6600', //  Standard Colour #46
                47 => 'FF666699', //  Standard Colour #47
                48 => 'FF969696', //  Standard Colour #48
                49 => 'FF003366', //  Standard Colour #49
                50 => 'FF339966', //  Standard Colour #50
                51 => 'FF003300', //  Standard Colour #51
                52 => 'FF333300', //  Standard Colour #52
                53 => 'FF993300', //  Standard Colour #53
                54 => 'FF993366', //  Standard Colour #54
                55 => 'FF333399', //  Standard Colour #55
                56 => 'FF333333', //  Standard Colour #56
            ];
        }

        if (isset(self::$indexedColors[$pIndex])) {
            return new self(self::$indexedColors[$pIndex]);
        }

        if ($background) {
            return new self(self::COLOR_WHITE);
        }

        return new self(self::COLOR_BLACK);
    }

    /**
     * Get hash code.
     *
     * @return string Hash code
     */
    public function getHashCode()
    {
        if ($this->isSupervisor) {
            return $this->getSharedComponent()->getHashCode();
        }

        return md5(
            $this->argb .
            __CLASS__
        );
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Worksheet;

use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\IComparable;

class BaseDrawing implements IComparable
{
    /**
     * Image counter.
     *
     * @var int
     */
    private static $imageCounter = 0;

    /**
     * Image index.
     *
     * @var int
     */
    private $imageIndex = 0;

    /**
     * Name.
     *
     * @var string
     */
    protected $name;

    /**
     * Description.
     *
     * @var string
     */
    protected $description;

    /**
     * Worksheet.
     *
     * @var Worksheet
     */
    protected $worksheet;

    /**
     * Coordinates.
     *
     * @var string
     */
    protected $coordinates;

    /**
     * Offset X.
     *
     * @var int
     */
    protected $offsetX;

    /**
     * Offset Y.
     *
     * @var int
     */
    protected $offsetY;

    /**
     * Width.
     *
     * @var int
     */
    protected $width;

    /**
     * Height.
     *
     * @var int
     */
    protected $height;

    /**
     * Proportional resize.
     *
     * @var bool
     */
    protected $resizeProportional;

    /**
     * Rotation.
     *
     * @var int
     */
    protected $rotation;

    /**
     * Shadow.
     *
     * @var Drawing\Shadow
     */
    protected $shadow;

    /**
     * Create a new BaseDrawing.
     */
    public function __construct()
    {
        // Initialise values
        $this->name = '';
        $this->description = '';
        $this->worksheet = null;
        $this->coordinates = 'A1';
        $this->offsetX = 0;
        $this->offsetY = 0;
        $this->width = 0;
        $this->height = 0;
        $this->resizeProportional = true;
        $this->rotation = 0;
        $this->shadow = new Drawing\Shadow();

        // Set image index
        ++self::$imageCounter;
        $this->imageIndex = self::$imageCounter;
    }

    /**
     * Get image index.
     *
     * @return int
     */
    public function getImageIndex()
    {
        return $this->imageIndex;
    }

    /**
     * Get Name.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set Name.
     *
     * @param string $pValue
     *
     * @return BaseDrawing
     */
    public function setName($pValue)
    {
        $this->name = $pValue;

        return $this;
    }

    /**
     * Get Description.
     *
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set Description.
     *
     * @param string $description
     *
     * @return BaseDrawing
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get Worksheet.
     *
     * @return Worksheet
     */
    public function getWorksheet()
    {
        return $this->worksheet;
    }

    /**
     * Set Worksheet.
     *
     * @param Worksheet $pValue
     * @param bool $pOverrideOld If a Worksheet has already been assigned, overwrite it and remove image from old Worksheet?
     *
     * @throws PhpSpreadsheetException
     *
     * @return BaseDrawing
     */
    public function setWorksheet(Worksheet $pValue = null, $pOverrideOld = false)
    {
        if ($this->worksheet === null) {
            // Add drawing to \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
            $this->worksheet = $pValue;
            $this->worksheet->getCell($this->coordinates);
            $this->worksheet->getDrawingCollection()->append($this);
        } else {
            if ($pOverrideOld) {
                // Remove drawing from old \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
                $iterator = $this->worksheet->getDrawingCollection()->getIterator();

                while ($iterator->valid()) {
                    if ($iterator->current()->getHashCode() == $this->getHashCode()) {
                        $this->worksheet->getDrawingCollection()->offsetUnset($iterator->key());
                        $this->worksheet = null;

                        break;
                    }
                }

                // Set new \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
                $this->setWorksheet($pValue);
            } else {
                throw new PhpSpreadsheetException('A Worksheet has already been assigned. Drawings can only exist on one \\PhpOffice\\PhpSpreadsheet\\Worksheet.');
            }
        }

        return $this;
    }

    /**
     * Get Coordinates.
     *
     * @return string
     */
    public function getCoordinates()
    {
        return $this->coordinates;
    }

    /**
     * Set Coordinates.
     *
     * @param string $pValue eg: 'A1'
     *
     * @return BaseDrawing
     */
    public function setCoordinates($pValue)
    {
        $this->coordinates = $pValue;

        return $this;
    }

    /**
     * Get OffsetX.
     *
     * @return int
     */
    public function getOffsetX()
    {
        return $this->offsetX;
    }

    /**
     * Set OffsetX.
     *
     * @param int $pValue
     *
     * @return BaseDrawing
     */
    public function setOffsetX($pValue)
    {
        $this->offsetX = $pValue;

        return $this;
    }

    /**
     * Get OffsetY.
     *
     * @return int
     */
    public function getOffsetY()
    {
        return $this->offsetY;
    }

    /**
     * Set OffsetY.
     *
     * @param int $pValue
     *
     * @return BaseDrawing
     */
    public function setOffsetY($pValue)
    {
        $this->offsetY = $pValue;

        return $this;
    }

    /**
     * Get Width.
     *
     * @return int
     */
    public function getWidth()
    {
        return $this->width;
    }

    /**
     * Set Width.
     *
     * @param int $pValue
     *
     * @return BaseDrawing
     */
    public function setWidth($pValue)
    {
        // Resize proportional?
        if ($this->resizeProportional && $pValue != 0) {
            $ratio = $this->height / ($this->width != 0 ? $this->width : 1);
            $this->height = round($ratio * $pValue);
        }

        // Set width
        $this->width = $pValue;

        return $this;
    }

    /**
     * Get Height.
     *
     * @return int
     */
    public function getHeight()
    {
        return $this->height;
    }

    /**
     * Set Height.
     *
     * @param int $pValue
     *
     * @return BaseDrawing
     */
    public function setHeight($pValue)
    {
        // Resize proportional?
        if ($this->resizeProportional && $pValue != 0) {
            $ratio = $this->width / ($this->height != 0 ? $this->height : 1);
            $this->width = round($ratio * $pValue);
        }

        // Set height
        $this->height = $pValue;

        return $this;
    }

    /**
     * Set width and height with proportional resize.
     *
     * Example:
     * <code>
     * $objDrawing->setResizeProportional(true);
     * $objDrawing->setWidthAndHeight(160,120);
     * </code>
     *
     * @author Vincent@luo MSN:kele_100@hotmail.com
     *
     * @param int $width
     * @param int $height
     *
     * @return BaseDrawing
     */
    public function setWidthAndHeight($width, $height)
    {
        $xratio = $width / ($this->width != 0 ? $this->width : 1);
        $yratio = $height / ($this->height != 0 ? $this->height : 1);
        if ($this->resizeProportional && !($width == 0 || $height == 0)) {
            if (($xratio * $this->height) < $height) {
                $this->height = ceil($xratio * $this->height);
                $this->width = $width;
            } else {
                $this->width = ceil($yratio * $this->width);
                $this->height = $height;
            }
        } else {
            $this->width = $width;
            $this->height = $height;
        }

        return $this;
    }

    /**
     * Get ResizeProportional.
     *
     * @return bool
     */
    public function getResizeProportional()
    {
        return $this->resizeProportional;
    }

    /**
     * Set ResizeProportional.
     *
     * @param bool $pValue
     *
     * @return BaseDrawing
     */
    public function setResizeProportional($pValue)
    {
        $this->resizeProportional = $pValue;

        return $this;
    }

    /**
     * Get Rotation.
     *
     * @return int
     */
    public function getRotation()
    {
        return $this->rotation;
    }

    /**
     * Set Rotation.
     *
     * @param int $pValue
     *
     * @return BaseDrawing
     */
    public function setRotation($pValue)
    {
        $this->rotation = $pValue;

        return $this;
    }

    /**
     * Get Shadow.
     *
     * @return Drawing\Shadow
     */
    public function getShadow()
    {
        return $this->shadow;
    }

    /**
     * Set Shadow.
     *
     * @param Drawing\Shadow $pValue
     *
     * @return BaseDrawing
     */
    public function setShadow(Drawing\Shadow $pValue = null)
    {
        $this->shadow = $pValue;

        return $this;
    }

    /**
     * Get hash code.
     *
     * @return string Hash code
     */
    public function getHashCode()
    {
        return md5(
            $this->name .
            $this->description .
            $this->worksheet->getHashCode() .
            $this->coordinates .
            $this->offsetX .
            $this->offsetY .
            $this->width .
            $this->height .
            $this->rotation .
            $this->shadow->getHashCode() .
            __CLASS__
        );
    }

    /**
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
     */
    public function __clone()
    {
        $vars = get_object_vars($this);
        foreach ($vars as $key => $value) {
            if (is_object($value)) {
                $this->$key = clone $value;
            } else {
                $this->$key = $value;
            }
        }
    }
}


<?php
/**
 * Created by PhpStorm.
 * User: joro
 * Date: 12.6.2017 г.
 * Time: 16:48 ч.
 */

namespace Omniship\Common;

use Omniship\Helper\Collection;
use Omniship\Interfaces\CodPaymentInterface;

class CodPaymentBag extends Collection
{

    /**
     * Create a new collection.
     *
     * @param  mixed  $items
     */
    public function __construct($items = [])
    {
        $items = array_map(function($item) {
            return !($item instanceof CodPaymentInterface) ? new CodPayment($item) : $item;
        }, $items);
        parent::__construct($items);
    }

    /**
     * Set the item at a given offset.
     *
     * @param  mixed  $key
     * @param  mixed  $value
     * @return void
     */
    public function offsetSet($key, $value)
    {
        if(!($value instanceof CodPaymentInterface)) {
            $value = new CodPayment($value);
        }
        parent::offsetSet($key, $value);
    }

    /**
     * Get all of the items in the collection.
     *
     * @return Item[]
     */
    public function all()
    {
        return parent::all();
    }
}


<?php

namespace PhpOffice\PhpSpreadsheet\Worksheet;

use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;

/**
 *
 * Paper size taken from Office Open XML Part 4 - Markup Language Reference, page 1988:.
 *
 * 1 = Letter paper (8.5 in. by 11 in.)
 * 2 = Letter small paper (8.5 in. by 11 in.)
 * 3 = Tabloid paper (11 in. by 17 in.)
 * 4 = Ledger paper (17 in. by 11 in.)
 * 5 = Legal paper (8.5 in. by 14 in.)
 * 6 = Statement paper (5.5 in. by 8.5 in.)
 * 7 = Executive paper (7.25 in. by 10.5 in.)
 * 8 = A3 paper (297 mm by 420 mm)
 * 9 = A4 paper (210 mm by 297 mm)
 * 10 = A4 small paper (210 mm by 297 mm)
 * 11 = A5 paper (148 mm by 210 mm)
 * 12 = B4 paper (250 mm by 353 mm)
 * 13 = B5 paper (176 mm by 250 mm)
 * 14 = Folio paper (8.5 in. by 13 in.)
 * 15 = Quarto paper (215 mm by 275 mm)
 * 16 = Standard paper (10 in. by 14 in.)
 * 17 = Standard paper (11 in. by 17 in.)
 * 18 = Note paper (8.5 in. by 11 in.)
 * 19 = #9 envelope (3.875 in. by 8.875 in.)
 * 20 = #10 envelope (4.125 in. by 9.5 in.)
 * 21 = #11 envelope (4.5 in. by 10.375 in.)
 * 22 = #12 envelope (4.75 in. by 11 in.)
 * 23 = #14 envelope (5 in. by 11.5 in.)
 * 24 = C paper (17 in. by 22 in.)
 * 25 = D paper (22 in. by 34 in.)
 * 26 = E paper (34 in. by 44 in.)
 * 27 = DL envelope (110 mm by 220 mm)
 * 28 = C5 envelope (162 mm by 229 mm)
 * 29 = C3 envelope (324 mm by 458 mm)
 * 30 = C4 envelope (229 mm by 324 mm)
 * 31 = C6 envelope (114 mm by 162 mm)
 * 32 = C65 envelope (114 mm by 229 mm)
 * 33 = B4 envelope (250 mm by 353 mm)
 * 34 = B5 envelope (176 mm by 250 mm)
 * 35 = B6 envelope (176 mm by 125 mm)
 * 36 = Italy envelope (110 mm by 230 mm)
 * 37 = Monarch envelope (3.875 in. by 7.5 in.).
 * 38 = 6 3/4 envelope (3.625 in. by 6.5 in.)
 * 39 = US standard fanfold (14.875 in. by 11 in.)
 * 40 = German standard fanfold (8.5 in. by 12 in.)
 * 41 = German legal fanfold (8.5 in. by 13 in.)
 * 42 = ISO B4 (250 mm by 353 mm)
 * 43 = Japanese double postcard (200 mm by 148 mm)
 * 44 = Standard paper (9 in. by 11 in.)
 * 45 = Standard paper (10 in. by 11 in.)
 * 46 = Standard paper (15 in. by 11 in.)
 * 47 = Invite envelope (220 mm by 220 mm)
 * 50 = Letter extra paper (9.275 in. by 12 in.)
 * 51 = Legal extra paper (9.275 in. by 15 in.)
 * 52 = Tabloid extra paper (11.69 in. by 18 in.)
 * 53 = A4 extra paper (236 mm by 322 mm)
 * 54 = Letter transverse paper (8.275 in. by 11 in.)
 * 55 = A4 transverse paper (210 mm by 297 mm)
 * 56 = Letter extra transverse paper (9.275 in. by 12 in.)
 * 57 = SuperA/SuperA/A4 paper (227 mm by 356 mm)
 * 58 = SuperB/SuperB/A3 paper (305 mm by 487 mm)
 * 59 = Letter plus paper (8.5 in. by 12.69 in.)
 * 60 = A4 plus paper (210 mm by 330 mm)
 * 61 = A5 transverse paper (148 mm by 210 mm)
 * 62 = JIS B5 transverse paper (182 mm by 257 mm)
 * 63 = A3 extra paper (322 mm by 445 mm)
 * 64 = A5 extra paper (174 mm by 235 mm)
 * 65 = ISO B5 extra paper (201 mm by 276 mm)
 * 66 = A2 paper (420 mm by 594 mm)
 * 67 = A3 transverse paper (297 mm by 420 mm)
 * 68 = A3 extra transverse paper (322 mm by 445 mm)
 *
 *
 * @category   PhpSpreadsheet
 *
 * @copyright  Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
 */
class PageSetup
{
    // Paper size
    const PAPERSIZE_LETTER = 1;
    const PAPERSIZE_LETTER_SMALL = 2;
    const PAPERSIZE_TABLOID = 3;
    const PAPERSIZE_LEDGER = 4;
    const PAPERSIZE_LEGAL = 5;
    const PAPERSIZE_STATEMENT = 6;
    const PAPERSIZE_EXECUTIVE = 7;
    const PAPERSIZE_A3 = 8;
    const PAPERSIZE_A4 = 9;
    const PAPERSIZE_A4_SMALL = 10;
    const PAPERSIZE_A5 = 11;
    const PAPERSIZE_B4 = 12;
    const PAPERSIZE_B5 = 13;
    const PAPERSIZE_FOLIO = 14;
    const PAPERSIZE_QUARTO = 15;
    const PAPERSIZE_STANDARD_1 = 16;
    const PAPERSIZE_STANDARD_2 = 17;
    const PAPERSIZE_NOTE = 18;
    const PAPERSIZE_NO9_ENVELOPE = 19;
    const PAPERSIZE_NO10_ENVELOPE = 20;
    const PAPERSIZE_NO11_ENVELOPE = 21;
    const PAPERSIZE_NO12_ENVELOPE = 22;
    const PAPERSIZE_NO14_ENVELOPE = 23;
    const PAPERSIZE_C = 24;
    const PAPERSIZE_D = 25;
    const PAPERSIZE_E = 26;
    const PAPERSIZE_DL_ENVELOPE = 27;
    const PAPERSIZE_C5_ENVELOPE = 28;
    const PAPERSIZE_C3_ENVELOPE = 29;
    const PAPERSIZE_C4_ENVELOPE = 30;
    const PAPERSIZE_C6_ENVELOPE = 31;
    const PAPERSIZE_C65_ENVELOPE = 32;
    const PAPERSIZE_B4_ENVELOPE = 33;
    const PAPERSIZE_B5_ENVELOPE = 34;
    const PAPERSIZE_B6_ENVELOPE = 35;
    const PAPERSIZE_ITALY_ENVELOPE = 36;
    const PAPERSIZE_MONARCH_ENVELOPE = 37;
    const PAPERSIZE_6_3_4_ENVELOPE = 38;
    const PAPERSIZE_US_STANDARD_FANFOLD = 39;
    const PAPERSIZE_GERMAN_STANDARD_FANFOLD = 40;
    const PAPERSIZE_GERMAN_LEGAL_FANFOLD = 41;
    const PAPERSIZE_ISO_B4 = 42;
    const PAPERSIZE_JAPANESE_DOUBLE_POSTCARD = 43;
    const PAPERSIZE_STANDARD_PAPER_1 = 44;
    const PAPERSIZE_STANDARD_PAPER_2 = 45;
    const PAPERSIZE_STANDARD_PAPER_3 = 46;
    const PAPERSIZE_INVITE_ENVELOPE = 47;
    const PAPERSIZE_LETTER_EXTRA_PAPER = 48;
    const PAPERSIZE_LEGAL_EXTRA_PAPER = 49;
    const PAPERSIZE_TABLOID_EXTRA_PAPER = 50;
    const PAPERSIZE_A4_EXTRA_PAPER = 51;
    const PAPERSIZE_LETTER_TRANSVERSE_PAPER = 52;
    const PAPERSIZE_A4_TRANSVERSE_PAPER = 53;
    const PAPERSIZE_LETTER_EXTRA_TRANSVERSE_PAPER = 54;
    const PAPERSIZE_SUPERA_SUPERA_A4_PAPER = 55;
    const PAPERSIZE_SUPERB_SUPERB_A3_PAPER = 56;
    const PAPERSIZE_LETTER_PLUS_PAPER = 57;
    const PAPERSIZE_A4_PLUS_PAPER = 58;
    const PAPERSIZE_A5_TRANSVERSE_PAPER = 59;
    const PAPERSIZE_JIS_B5_TRANSVERSE_PAPER = 60;
    const PAPERSIZE_A3_EXTRA_PAPER = 61;
    const PAPERSIZE_A5_EXTRA_PAPER = 62;
    const PAPERSIZE_ISO_B5_EXTRA_PAPER = 63;
    const PAPERSIZE_A2_PAPER = 64;
    const PAPERSIZE_A3_TRANSVERSE_PAPER = 65;
    const PAPERSIZE_A3_EXTRA_TRANSVERSE_PAPER = 66;

    // Page orientation
    const ORIENTATION_DEFAULT = 'default';
    const ORIENTATION_LANDSCAPE = 'landscape';
    const ORIENTATION_PORTRAIT = 'portrait';

    // Print Range Set Method
    const SETPRINTRANGE_OVERWRITE = 'O';
    const SETPRINTRANGE_INSERT = 'I';

    /**
     * Paper size.
     *
     * @var int
     */
    private $paperSize = self::PAPERSIZE_LETTER;

    /**
     * Orientation.
     *
     * @var string
     */
    private $orientation = self::ORIENTATION_DEFAULT;

    /**
     * Scale (Print Scale).
     *
     * Print scaling. Valid values range from 10 to 400
     * This setting is overridden when fitToWidth and/or fitToHeight are in use
     *
     * @var int?
     */
    private $scale = 100;

    /**
     * Fit To Page
     * Whether scale or fitToWith / fitToHeight applies.
     *
     * @var bool
     */
    private $fitToPage = false;

    /**
     * Fit To Height
     * Number of vertical pages to fit on.
     *
     * @var int?
     */
    private $fitToHeight = 1;

    /**
     * Fit To Width
     * Number of horizontal pages to fit on.
     *
     * @var int?
     */
    private $fitToWidth = 1;

    /**
     * Columns to repeat at left.
     *
     * @var array Containing start column and end column, empty array if option unset
     */
    private $columnsToRepeatAtLeft = ['', ''];

    /**
     * Rows to repeat at top.
     *
     * @var array Containing start row number and end row number, empty array if option unset
     */
    private $rowsToRepeatAtTop = [0, 0];

    /**
     * Center page horizontally.
     *
     * @var bool
     */
    private $horizontalCentered = false;

    /**
     * Center page vertically.
     *
     * @var bool
     */
    private $verticalCentered = false;

    /**
     * Print area.
     *
     * @var string
     */
    private $printArea;

    /**
     * First page number.
     *
     * @var int
     */
    private $firstPageNumber;

    /**
     * Create a new PageSetup.
     */
    public function __construct()
    {
    }

    /**
     * Get Paper Size.
     *
     * @return int
     */
    public function getPaperSize()
    {
        return $this->paperSize;
    }

    /**
     * Set Paper Size.
     *
     * @param int $pValue see self::PAPERSIZE_*
     *
     * @return PageSetup
     */
    public function setPaperSize($pValue)
    {
        $this->paperSize = $pValue;

        return $this;
    }

    /**
     * Get Orientation.
     *
     * @return string
     */
    public function getOrientation()
    {
        return $this->orientation;
    }

    /**
     * Set Orientation.
     *
     * @param string $pValue see self::ORIENTATION_*
     *
     * @return PageSetup
     */
    public function setOrientation($pValue)
    {
        $this->orientation = $pValue;

        return $this;
    }

    /**
     * Get Scale.
     *
     * @return int?
     */
    public function getScale()
    {
        return $this->scale;
    }

    /**
     * Set Scale.
     * Print scaling. Valid values range from 10 to 400
     * This setting is overridden when fitToWidth and/or fitToHeight are in use.
     *
     * @param null|int $pValue
     * @param bool $pUpdate Update fitToPage so scaling applies rather than fitToHeight / fitToWidth
     *
     * @throws PhpSpreadsheetException
     *
     * @return PageSetup
     */
    public function setScale($pValue, $pUpdate = true)
    {
        // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
        // but it is apparently still able to handle any scale >= 0, where 0 results in 100
        if (($pValue >= 0) || $pValue === null) {
            $this->scale = $pValue;
            if ($pUpdate) {
                $this->fitToPage = false;
            }
        } else {
            throw new PhpSpreadsheetException('Scale must not be negative');
        }

        return $this;
    }

    /**
     * Get Fit To Page.
     *
     * @return bool
     */
    public function getFitToPage()
    {
        return $this->fitToPage;
    }

    /**
     * Set Fit To Page.
     *
     * @param bool $pValue
     *
     * @return PageSetup
     */
    public function setFitToPage($pValue)
    {
        $this->fitToPage = $pValue;

        return $this;
    }

    /**
     * Get Fit To Height.
     *
     * @return int?
     */
    public function getFitToHeight()
    {
        return $this->fitToHeight;
    }

    /**
     * Set Fit To Height.
     *
     * @param null|int $pValue
     * @param bool $pUpdate Update fitToPage so it applies rather than scaling
     *
     * @return PageSetup
     */
    public function setFitToHeight($pValue, $pUpdate = true)
    {
        $this->fitToHeight = $pValue;
        if ($pUpdate) {
            $this->fitToPage = true;
        }

        return $this;
    }

    /**
     * Get Fit To Width.
     *
     * @return int?
     */
    public function getFitToWidth()
    {
        return $this->fitToWidth;
    }

    /**
     * Set Fit To Width.
     *
     * @param null|int $pValue
     * @param bool $pUpdate Update fitToPage so it applies rather than scaling
     *
     * @return PageSetup
     */
    public function setFitToWidth($pValue, $pUpdate = true)
    {
        $this->fitToWidth = $pValue;
        if ($pUpdate) {
            $this->fitToPage = true;
        }

        return $this;
    }

    /**
     * Is Columns to repeat at left set?
     *
     * @return bool
     */
    public function isColumnsToRepeatAtLeftSet()
    {
        if (is_array($this->columnsToRepeatAtLeft)) {
            if ($this->columnsToRepeatAtLeft[0] != '' && $this->columnsToRepeatAtLeft[1] != '') {
                return true;
            }
        }

        return false;
    }

    /**
     * Get Columns to repeat at left.
     *
     * @return array Containing start column and end column, empty array if option unset
     */
    public function getColumnsToRepeatAtLeft()
    {
        return $this->columnsToRepeatAtLeft;
    }

    /**
     * Set Columns to repeat at left.
     *
     * @param array $pValue Containing start column and end column, empty array if option unset
     *
     * @return PageSetup
     */
    public function setColumnsToRepeatAtLeft(array $pValue)
    {
        $this->columnsToRepeatAtLeft = $pValue;

        return $this;
    }

    /**
     * Set Columns to repeat at left by start and end.
     *
     * @param string $pStart eg: 'A'
     * @param string $pEnd eg: 'B'
     *
     * @return PageSetup
     */
    public function setColumnsToRepeatAtLeftByStartAndEnd($pStart, $pEnd)
    {
        $this->columnsToRepeatAtLeft = [$pStart, $pEnd];

        return $this;
    }

    /**
     * Is Rows to repeat at top set?
     *
     * @return bool
     */
    public function isRowsToRepeatAtTopSet()
    {
        if (is_array($this->rowsToRepeatAtTop)) {
            if ($this->rowsToRepeatAtTop[0] != 0 && $this->rowsToRepeatAtTop[1] != 0) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get Rows to repeat at top.
     *
     * @return array Containing start column and end column, empty array if option unset
     */
    public function getRowsToRepeatAtTop()
    {
        return $this->rowsToRepeatAtTop;
    }

    /**
     * Set Rows to repeat at top.
     *
     * @param array $pValue Containing start column and end column, empty array if option unset
     *
     * @return PageSetup
     */
    public function setRowsToRepeatAtTop(array $pValue)
    {
        $this->rowsToRepeatAtTop = $pValue;

        return $this;
    }

    /**
     * Set Rows to repeat at top by start and end.
     *
     * @param int $pStart eg: 1
     * @param int $pEnd eg: 1
     *
     * @return PageSetup
     */
    public function setRowsToRepeatAtTopByStartAndEnd($pStart, $pEnd)
    {
        $this->rowsToRepeatAtTop = [$pStart, $pEnd];

        return $this;
    }

    /**
     * Get center page horizontally.
     *
     * @return bool
     */
    public function getHorizontalCentered()
    {
        return $this->horizontalCentered;
    }

    /**
     * Set center page horizontally.
     *
     * @param bool $value
     *
     * @return PageSetup
     */
    public function setHorizontalCentered($value)
    {
        $this->horizontalCentered = $value;

        return $this;
    }

    /**
     * Get center page vertically.
     *
     * @return bool
     */
    public function getVerticalCentered()
    {
        return $this->verticalCentered;
    }

    /**
     * Set center page vertically.
     *
     * @param bool $value
     *
     * @return PageSetup
     */
    public function setVerticalCentered($value)
    {
        $this->verticalCentered = $value;

        return $this;
    }

    /**
     * Get print area.
     *
     * @param int $index Identifier for a specific print area range if several ranges have been set
     *                            Default behaviour, or a index value of 0, will return all ranges as a comma-separated string
     *                            Otherwise, the specific range identified by the value of $index will be returned
     *                            Print areas are numbered from 1
     *
     * @throws PhpSpreadsheetException
     *
     * @return string
     */
    public function getPrintArea($index = 0)
    {
        if ($index == 0) {
            return $this->printArea;
        }
        $printAreas = explode(',', $this->printArea);
        if (isset($printAreas[$index - 1])) {
            return $printAreas[$index - 1];
        }

        throw new PhpSpreadsheetException('Requested Print Area does not exist');
    }

    /**
     * Is print area set?
     *
     * @param int $index Identifier for a specific print area range if several ranges have been set
     *                            Default behaviour, or an index value of 0, will identify whether any print range is set
     *                            Otherwise, existence of the range identified by the value of $index will be returned
     *                            Print areas are numbered from 1
     *
     * @return bool
     */
    public function isPrintAreaSet($index = 0)
    {
        if ($index == 0) {
            return $this->printArea !== null;
        }
        $printAreas = explode(',', $this->printArea);

        return isset($printAreas[$index - 1]);
    }

    /**
     * Clear a print area.
     *
     * @param int $index Identifier for a specific print area range if several ranges have been set
     *                            Default behaviour, or an index value of 0, will clear all print ranges that are set
     *                            Otherwise, the range identified by the value of $index will be removed from the series
     *                            Print areas are numbered from 1
     *
     * @return PageSetup
     */
    public function clearPrintArea($index = 0)
    {
        if ($index == 0) {
            $this->printArea = null;
        } else {
            $printAreas = explode(',', $this->printArea);
            if (isset($printAreas[$index - 1])) {
                unset($printAreas[$index - 1]);
                $this->printArea = implode(',', $printAreas);
            }
        }

        return $this;
    }

    /**
     * Set print area. e.g. 'A1:D10' or 'A1:D10,G5:M20'.
     *
     * @param string $value
     * @param int $index Identifier for a specific print area range allowing several ranges to be set
     *                            When the method is "O"verwrite, then a positive integer index will overwrite that indexed
     *                                entry in the print areas list; a negative index value will identify which entry to
     *                                overwrite working bacward through the print area to the list, with the last entry as -1.
     *                                Specifying an index value of 0, will overwrite all existing print ranges.
     *                            When the method is "I"nsert, then a positive index will insert after that indexed entry in
     *                                the print areas list, while a negative index will insert before the indexed entry.
     *                                Specifying an index value of 0, will always append the new print range at the end of the
     *                                list.
     *                            Print areas are numbered from 1
     * @param string $method Determines the method used when setting multiple print areas
     *                            Default behaviour, or the "O" method, overwrites existing print area
     *                            The "I" method, inserts the new print area before any specified index, or at the end of the list
     *
     * @throws PhpSpreadsheetException
     *
     * @return PageSetup
     */
    public function setPrintArea($value, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
    {
        if (strpos($value, '!') !== false) {
            throw new PhpSpreadsheetException('Cell coordinate must not specify a worksheet.');
        } elseif (strpos($value, ':') === false) {
            throw new PhpSpreadsheetException('Cell coordinate must be a range of cells.');
        } elseif (strpos($value, '$') !== false) {
            throw new PhpSpreadsheetException('Cell coordinate must not be absolute.');
        }
        $value = strtoupper($value);

        if ($method == self::SETPRINTRANGE_OVERWRITE) {
            if ($index == 0) {
                $this->printArea = $value;
            } else {
                $printAreas = explode(',', $this->printArea);
                if ($index < 0) {
                    $index = count($printAreas) - abs($index) + 1;
                }
                if (($index <= 0) || ($index > count($printAreas))) {
                    throw new PhpSpreadsheetException('Invalid index for setting print range.');
                }
                $printAreas[$index - 1] = $value;
                $this->printArea = implode(',', $printAreas);
            }
        } elseif ($method == self::SETPRINTRANGE_INSERT) {
            if ($index == 0) {
                $this->printArea .= ($this->printArea == '') ? $value : ',' . $value;
            } else {
                $printAreas = explode(',', $this->printArea);
                if ($index < 0) {
                    $index = abs($index) - 1;
                }
                if ($index > count($printAreas)) {
                    throw new PhpSpreadsheetException('Invalid index for setting print range.');
                }
                $printAreas = array_merge(array_slice($printAreas, 0, $index), [$value], array_slice($printAreas, $index));
                $this->printArea = implode(',', $printAreas);
            }
        } else {
            throw new PhpSpreadsheetException('Invalid method for setting print range.');
        }

        return $this;
    }

    /**
     * Add a new print area (e.g. 'A1:D10' or 'A1:D10,G5:M20') to the list of print areas.
     *
     * @param string $value
     * @param int $index Identifier for a specific print area range allowing several ranges to be set
     *                            A positive index will insert after that indexed entry in the print areas list, while a
     *                                negative index will insert before the indexed entry.
     *                                Specifying an index value of 0, will always append the new print range at the end of the
     *                                list.
     *                            Print areas are numbered from 1
     *
     * @throws PhpSpreadsheetException
     *
     * @return PageSetup
     */
    public function addPrintArea($value, $index = -1)
    {
        return $this->setPrintArea($value, $index, self::SETPRINTRANGE_INSERT);
    }

    /**
     * Set print area.
     *
     * @param int $column1 Column 1
     * @param int $row1 Row 1
     * @param int $column2 Column 2
     * @param int $row2 Row 2
     * @param int $index Identifier for a specific print area range allowing several ranges to be set
     *                                When the method is "O"verwrite, then a positive integer index will overwrite that indexed
     *                                    entry in the print areas list; a negative index value will identify which entry to
     *                                    overwrite working backward through the print area to the list, with the last entry as -1.
     *                                    Specifying an index value of 0, will overwrite all existing print ranges.
     *                                When the method is "I"nsert, then a positive index will insert after that indexed entry in
     *                                    the print areas list, while a negative index will insert before the indexed entry.
     *                                    Specifying an index value of 0, will always append the new print range at the end of the
     *                                    list.
     *                                Print areas are numbered from 1
     * @param string $method Determines the method used when setting multiple print areas
     *                                Default behaviour, or the "O" method, overwrites existing print area
     *                                The "I" method, inserts the new print area before any specified index, or at the end of the list
     *
     * @throws PhpSpreadsheetException
     *
     * @return PageSetup
     */
    public function setPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
    {
        return $this->setPrintArea(
            Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
            $index,
            $method
        );
    }

    /**
     * Add a new print area to the list of print areas.
     *
     * @param int $column1 Start Column for the print area
     * @param int $row1 Start Row for the print area
     * @param int $column2 End Column for the print area
     * @param int $row2 End Row for the print area
     * @param int $index Identifier for a specific print area range allowing several ranges to be set
     *                                A positive index will insert after that indexed entry in the print areas list, while a
     *                                    negative index will insert before the indexed entry.
     *                                    Specifying an index value of 0, will always append the new print range at the end of the
     *                                    list.
     *                                Print areas are numbered from 1
     *
     * @throws PhpSpreadsheetException
     *
     * @return PageSetup
     */
    public function addPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = -1)
    {
        return $this->setPrintArea(
            Coordinate::stringFromColumnIndex($column1) . $row1 . ':' . Coordinate::stringFromColumnIndex($column2) . $row2,
            $index,
            self::SETPRINTRANGE_INSERT
        );
    }

    /**
     * Get first page number.
     *
     * @return int
     */
    public function getFirstPageNumber()
    {
        return $this->firstPageNumber;
    }

    /**
     * Set first page number.
     *
     * @param int $value
     *
     * @return PageSetup
     */
    public function setFirstPageNumber($value)
    {
        $this->firstPageNumber = $value;

        return $this;
    }

    /**
     * Reset first page number.
     *
     * @return PageSetup
     */
    public function resetFirstPageNumber()
    {
        return $this->setFirstPageNumber(null);
    }

    /**
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
     */
    public function __clone()
    {
        $vars = get_object_vars($this);
        foreach ($vars as $key => $value) {
            if (is_object($value)) {
                $this->$key = clone $value;
            } else {
                $this->$key = $value;
            }
        }
    }
}


<?php
/**
 * Shipping address
 */

namespace Omniship\Common;

use Carbon\Carbon;
use Omniship\Address\City;
use Omniship\Address\Country;
use Omniship\Address\Office;
use Omniship\Address\Quarter;
use Omniship\Address\State;
use Omniship\Address\Street;
use Omniship\Exceptions\InvalidArgumentException;
use Omniship\Interfaces\ArrayableInterface;
use Omniship\Interfaces\JsonableInterface;
use Omniship\Traits\Exceptions;
use Omniship\Traits\Parameters;
use Omniship\Interfaces\AddressInterface;
//formatter

use CommerceGuys\Addressing\Model\Address AS AddressFormatter;
use CommerceGuys\Addressing\Formatter\DefaultFormatter;
use CommerceGuys\Addressing\Repository\AddressFormatRepository;
use CommerceGuys\Addressing\Repository\CountryRepository;
use CommerceGuys\Addressing\Repository\SubdivisionRepository;

class Address implements AddressInterface, ArrayableInterface, \JsonSerializable, JsonableInterface
{

    use Parameters, Exceptions;

    const INVALID_ARGUMENTS = [
        '10001' => 'Invalid arguments for method Omniship\Common\Address::setCountry',
        '10002' => 'Invalid arguments for method Omniship\Common\Address::setState',
        '10003' => 'Invalid arguments for method Omniship\Common\Address::setCity',
        '10004' => 'Invalid arguments for method Omniship\Common\Address::setTimeZone',
        '10005' => 'Invalid arguments for method Omniship\Common\Address::setQuarter',
        '10006' => 'Invalid arguments for method Omniship\Common\Address::setStreet',
        '10007' => 'Invalid arguments for method Omniship\Common\Address::setOffice',
    ];

    /**
     * @var array
     */
    protected $time_zones;

    /**
     * @var array
     */
    protected $full_name_template = '{first_name} {last_name}';

    /**
     * @var array
     */
    protected $street_format = '{street_name} {street_number}';

    /**
     * Get the address id
     * @return mixed
     */
    public function getId()
    {
        return $this->getParameter('id');
    }

    /**
     * Set address id
     * @param $value
     * @return $this
     */
    public function setId($value)
    {
        return $this->setParameter('id', $value);
    }

    /**
     * Get the address country
     * @return Country
     */
    public function getCountry()
    {
        return $this->getParameter('country');
    }

    /**
     * Set the address country
     * @param Country|array $country
     * @return $this
     * @throws InvalidArgumentException
     */
    public function setCountry($country)
    {
        if (!($country instanceof Country)) {
            $country = new Country($country);
        }
        if ($country->isEmpty()) {
            $this->invalidArguments('10001');
        }
        return $this->setParameter('country', $country);
    }

    /**
     * Get the address state
     * @return State
     */
    public function getState()
    {
        return $this->getParameter('state');
    }

    /**
     * Set the address state
     * @param State|array $state
     * @return $this
     * @throws InvalidArgumentException
     */
    public function setState($state)
    {
        if(!$state) {
            return $this;
        }
        if (!($state instanceof State)) {
            $state = new State($state);
        }
        if ($state->isEmpty()) {
            $this->invalidArguments('10002');
        }
        return $this->setParameter('state', $state);
    }

    /**
     * Get the address city
     * @return City
     */
    public function getCity()
    {
        return $this->getParameter('city');
    }

    /**
     * Set the address city
     * @param City|array $city
     * @return $this
     * @throws InvalidArgumentException
     */
    public function setCity($city)
    {
        if (!($city instanceof City)) {
            $city = new City($city);
        }
        if ($city->isEmpty()) {
            $this->invalidArguments('10003');
        }
        return $this->setParameter('city', $city);
    }

    /**
     * Get the address quarter
     * @return Quarter
     */
    public function getQuarter()
    {
        return $this->getParameter('quarter');
    }

    /**
     * Set the address quarter
     * @param Quarter|array $quarter
     * @return $this
     * @throws InvalidArgumentException
     */
    public function setQuarter($quarter)
    {
        if(!$quarter) {
            return $this;
        }
        if (!($quarter instanceof Quarter)) {
            $quarter = new Quarter($quarter);
        }
        if ($quarter->isEmpty()) {
            $this->invalidArguments('10005');
        }
        return $this->setParameter('quarter', $quarter);
    }

    /**
     * Get the address street
     * @return Street
     */
    public function getStreet()
    {
        return $this->getParameter('street');
    }

    /**
     * Set the address street
     * @param Street|array $street
     * @return $this
     * @throws InvalidArgumentException
     */
    public function setStreet($street)
    {
        if(!$street) {
            return $this;
        }
        if (!($street instanceof Street)) {
            $street = new Street($street);
        }
        if ($street->isEmpty()) {
            $this->invalidArguments('10006');
        }
        return $this->setParameter('street', $street);
    }

    /**
     * Get the address office
     * @return Office
     */
    public function getOffice()
    {
        return $this->getParameter('office');
    }

    /**
     * Set the address street
     * @param Office|array $office
     * @return $this
     * @throws InvalidArgumentException
     */
    public function setOffice($office)
    {
        if(!$office) {
            return $this;
        }
        if (!($office instanceof Office)) {
            $office = new Office($office);
        }
        if ($office->isEmpty()) {
            $this->invalidArguments('10007');
        }
        return $this->setParameter('office', $office);
    }

    /**
     * Get the address street num
     * @return string|mixed
     */
    public function getStreetNumber()
    {
        return $this->getParameter('street_number');
    }

    /**
     * Set the address street num
     * @param $street_number
     * @return $this
     */
    public function setStreetNumber($street_number)
    {
        return $this->setParameter('street_number', $street_number);
    }

    /**
     * Get the address post code
     * @return string|mixed
     */
    public function getPostCode()
    {
        return $this->getParameter('post_code');
    }

    /**
     * Set the address post code
     * @param $post_code
     * @return $this
     */
    public function setPostCode($post_code)
    {
        return $this->setParameter('post_code', $post_code);
    }

    /**
     * Get the address line 1
     * @return string|mixed
     */
    public function getAddress1()
    {
        return $this->getParameter('address1');
    }

    /**
     * Set the address line 1
     * @param string $address
     * @return $this
     */
    public function setAddress1($address)
    {
        return $this->setParameter('address1', $address);
    }

    /**
     * Get the address line 2
     * @return string|mixed
     */
    public function getAddress2()
    {
        return $this->getParameter('address2');
    }

    /**
     * Set the address line 2
     * @param string $address
     * @return $this
     */
    public function setAddress2($address)
    {
        return $this->setParameter('address2', $address);
    }

    /**
     * Get the address line 3
     * @return string|mixed
     */
    public function getAddress3()
    {
        return $this->getParameter('address3');
    }

    /**
     * Set the address line 3
     * @param string $address
     * @return $this
     */
    public function setAddress3($address)
    {
        return $this->setParameter('address3', $address);
    }

    /**
     * Get the address note
     * @return string|mixed
     */
    public function getNote()
    {
        return $this->getParameter('note');
    }

    /**
     * Set the address note
     * @param string $address
     * @return $this
     */
    public function setNote($address)
    {
        return $this->setParameter('note', $address);
    }

    /**
     * Get the address phone
     * @return string|mixed
     */
    public function getPhone()
    {
        return $this->getParameter('phone');
    }

    /**
     * Set the address phone
     * @param string $phone
     * @return $this
     */
    public function setPhone($phone)
    {
        return $this->setParameter('phone', $phone);
    }

    /**
     * Get the address building
     * @return string|mixed
     */
    public function getBuilding()
    {
        return $this->getParameter('building');
    }

    /**
     * Set the address building
     * @param string $building
     * @return $this
     */
    public function setBuilding($building)
    {
        return $this->setParameter('building', $building);
    }

    /**
     * Get the address entrance
     * @return string|mixed
     */
    public function getEntrance()
    {
        return $this->getParameter('entrance');
    }

    /**
     * Set the address entrance
     * @param string $entrance
     * @return $this
     */
    public function setEntrance($entrance)
    {
        return $this->setParameter('entrance', $entrance);
    }

    /**
     * Get the address floor
     * @return string|mixed
     */
    public function getFloor()
    {
        return $this->getParameter('floor');
    }

    /**
     * Set the address floor
     * @param string $floor
     * @return $this
     */
    public function setFloor($floor)
    {
        return $this->setParameter('floor', $floor);
    }

    /**
     * Get the address apartment
     * @return string|mixed
     */
    public function getApartment()
    {
        return $this->getParameter('apartment');
    }

    /**
     * Set the address apartment
     * @param string $apartment
     * @return $this
     */
    public function setApartment($apartment)
    {
        return $this->setParameter('apartment', $apartment);
    }

    /**
     * Get the address first name
     * @return string|mixed
     */
    public function getFirstName()
    {
        return $this->getParameter('first_name');
    }

    /**
     * Set the address first name
     * @param string $first_name
     * @return $this
     */
    public function setFirstName($first_name)
    {
        return $this->setParameter('first_name', $first_name);
    }

    /**
     * Get the address last name
     * @return string|mixed
     */
    public function getLastName()
    {
        return $this->getParameter('last_name');
    }

    /**
     * Set the address last name
     * @param string $last_name
     * @return $this
     */
    public function setLastName($last_name)
    {
        return $this->setParameter('last_name', $last_name);
    }

    /**
     * Get the address company name
     * @return string|mixed
     */
    public function getCompanyName()
    {
        return $this->getParameter('company_name');
    }

    /**
     * Set the address company name
     * @param string $company_name
     * @return $this
     */
    public function setCompanyName($company_name)
    {
        return $this->setParameter('company_name', $company_name);
    }

    /**
     * Get the address company vat
     * @return string|mixed
     */
    public function getCompanyVat()
    {
        return $this->getParameter('company_vat');
    }

    /**
     * Set the address company vat
     * @param string $company_vat
     * @return $this
     */
    public function setCompanyVat($company_vat)
    {
        return $this->setParameter('company_vat', $company_vat);
    }

    /**
     * Get the address provider
     * @return string|mixed
     */
    public function getProvider()
    {
        return $this->getParameter('provider');
    }

    /**
     * Set the address provider
     * @param string $provider
     * @return $this
     */
    public function setProvider($provider)
    {
        return $this->setParameter('provider', $provider);
    }

    /**
     * Get the address other text
     * @return string|null
     */
    public function getText()
    {
        return $this->getParameter('text');
    }

    /**
     * Set the address other text
     * @param string $text
     * @return $this
     */
    public function setText($text)
    {
        return $this->setParameter('text', $text);
    }

    /**
     * Get the address latitude
     * @return float|null
     */
    public function getLatitude()
    {
        return $this->getParameter('latitude');
    }

    /**
     * Set the address latitude
     * @param float $latitude
     * @return $this
     */
    public function setLatitude($latitude)
    {
        return $this->setParameter('latitude', $latitude);
    }

    /**
     * Get the address longitude
     * @return float|null
     */
    public function getLongitude()
    {
        return $this->getParameter('longitude');
    }

    /**
     * Set the address longitude
     * @param float $longitude
     * @return $this
     */
    public function setLongitude($longitude)
    {
        return $this->setParameter('longitude', $longitude);
    }

    /**
     * Get the address local
     * @return float|null
     */
    public function getLocal()
    {
        return $this->getParameter('local');
    }

    /**
     * Set the address local
     * @param float $local
     * @return $this
     */
    public function setLocal($local)
    {
        return $this->setParameter('local', $local);
    }

    /**
     * Get the address time zone
     * @return string|null
     */
    public function getTimeZone()
    {
        return $this->getParameter('timezone');
    }

    /**
     * Set the address time zone
     * @param string|null $timezone
     * @return $this
     */
    public function setTimeZone($timezone)
    {
        if(!$timezone) {
            return $this;
        }
        try {
            Carbon::now($timezone);
        } catch (\Exception $e) {
            $this->invalidArguments('10004', sprintf('Invalid timezone set "%s"', $timezone));
        }
        return $this->setParameter('timezone', $timezone);
    }

    /**
     * Get the full name from template
     * @return string|null
     */
    public function getFullName()
    {
        return str_replace([
            '{first_name}',
            '{last_name}'
        ], [
            $this->getFirstName(),
            $this->getLastName(),
        ], $this->getTemplateFullName());
    }

    /**
     * Get template for full name
     * @return mixed
     */
    public function getTemplateFullName()
    {
        return $this->full_name_template;
    }

    /**
     * Set template for full name
     * @param $value
     * @return $this
     */
    public function setTemplateFullName($value)
    {
        $this->full_name_template = $value;
        return $this;
    }

    /**
     * Get template for street
     * @return mixed
     */
    public function getTemplateStreet()
    {
        return $this->street_format;
    }

    /**
     * Set template for street
     * @param $value
     * @return $this
     */
    public function setTemplateStreet($value)
    {
        $this->street_format = $value;
        return $this;
    }

    public function format($html = true, $with_recipient = true) {
        $addressFormatRepository = new AddressFormatRepository();
        $countryRepository = new CountryRepository();
        $subdivisionRepository = new SubdivisionRepository();
        $locale = $this->getLocal() ? $this->getLocal() : $this->getCountry()->getIso2();
        $formatter = new DefaultFormatter($addressFormatRepository, $countryRepository, $subdivisionRepository, $locale, [
            'html' => $html
        ]);
        // Options passed to the constructor or setOption / setOptions allow turning
        // off html rendering, customizing the wrapper element and its attributes.

        $address = new AddressFormatter($this->getCountry()->getIso2() ? : 'BG');
        if($with_recipient) {
            //add company or names to address
            if ($company = $this->getCompanyName()) {
                $address = $address->withRecipient($company);
            }
            if ($recipient = $this->getFullName()) {
                $address = $address->withRecipient($recipient);
            }
        }
        //add state to address
        if($state = $this->getState()) {
            $address = $address->withAdministrativeArea($state->getIso2());
        }
        //add city to address
        if($city = $this->getCity()) {
            $address = $address->withLocality($city->getName());
        }
        //add post code to address
        if($postal_code = $this->getPostCode()) {
            $address = $address->withPostalCode($postal_code);
        }
        //if address is office return formatted address
        if(!is_null($office = $this->getOffice())) {
            $address = $address->withAddressLine1($office->getName());
            return $formatter->format($address);
        }

        $line1 = '';
        if(!is_null($street = $this->getStreet())) {
            $number = $this->getStreetNumber();
            $line1 .= trim(str_replace([
                '{street_name}',
                '{street_number}'
            ], [
                $street->getName(),
                $number
            ], $this->getTemplateStreet()));
        } else if(!is_null($quarter = $this->getQuarter())) {
            $line1 .= $quarter->getName();
            if(!empty($number = $this->getStreetNumber())) {
                $line1 .= ' ' . $number;
            }
        }
        if($other = implode(' - ', array_filter([$this->getBuilding(), $this->getEntrance(), $this->getFloor(), $this->getApartment()]))) {
            $line1 .= ($line1 ? ' / ' : '') . $other;
        }
        if($line1) {
            $address = $address->withAddressLine1($line1);
        }
        if($lines = implode("\n", array_filter([$this->getAddress1(), $this->getAddress2(), $this->getAddress3()]))) {
            $address = $address->withAddressLine2($lines);
        }

        return $formatter->format($address);
    }

}


<?php

namespace RodrigoPedra\ClearSaleID\Entity\Request;

use DateTime;
use RodrigoPedra\ClearSaleID\Entity\XmlEntityInterface;
use XMLWriter;

class CustomerShippingData extends AbstractCustomer implements XmlEntityInterface
{
    /**
     * @param  string                                           $id
     * @param  string                                           $type
     * @param  string                                           $legalDocument
     * @param  string                                           $name
     * @param  \RodrigoPedra\ClearSaleID\Entity\Request\Address $address
     * @param  \RodrigoPedra\ClearSaleID\Entity\Request\Phone   $phone
     * @param  \DateTime|null                                   $birthDate
     *
     * @return \RodrigoPedra\ClearSaleID\Entity\Request\CustomerShippingData
     */
    public static function create(
        $id,
        $type,
        $legalDocument,
        $name,
        Address $address,
        $phone,
        DateTime $birthDate = null
    ) {
        $instance = new self;

        $instance->setId( $id );
        $instance->setType( $type );
        $instance->setLegalDocument1( $legalDocument );
        $instance->setName( $name );
        $instance->setAddress( $address );
        $instance->addPhone( $phone );

        if (!empty( $birthDate )) {
            $instance->setBirthDate( $birthDate );
        }

        return $instance;
    }

    /**
     * @param  \XMLWriter $XMLWriter
     *
     * @throws \RodrigoPedra\ClearSaleID\Exception\RequiredFieldException
     */
    public function toXML( XMLWriter $XMLWriter )
    {
        $XMLWriter->startElement( 'DadosEntrega' );

        parent::toXML( $XMLWriter );

        $XMLWriter->endElement();
    }
}


<?php

namespace RodrigoPedra\ClearSaleID\Service;

use RodrigoPedra\ClearSaleID\Entity\Request\Order as OrderRequest;
use RodrigoPedra\ClearSaleID\Entity\Response\PackageStatus;
use RodrigoPedra\ClearSaleID\Entity\Response\UpdateOrderStatus;
use XMLWriter;

class Integration
{
    /** @var  \RodrigoPedra\ClearSaleID\Service\Connector */
    protected $connector;

    /**
     * Construtor para gerar a integração com a ClearSale
     *
     * @param  \RodrigoPedra\ClearSaleID\Service\Connector $connector
     */
    public function __construct( Connector $connector )
    {
        $this->connector = $connector;
    }

    /**
     * Método para envio de um pedido
     *
     * @param  \RodrigoPedra\ClearSaleID\Entity\Request\Order $order
     *
     * @return \RodrigoPedra\ClearSaleID\Entity\Response\PackageStatus
     * @throws \RodrigoPedra\ClearSaleID\Exception\RequiredFieldException
     * @throws \RodrigoPedra\ClearSaleID\Exception\UnexpectedErrorException
     */
    public function sendOrder( OrderRequest $order )
    {

        $function   = 'SubmitInfo';
        $parameters = [
            'entityCode' => $this->connector->getEntityCode(),
            'xmlDados'   => $this->createRequestOrderXML( $order ),
        ];

        $response = $this->connector->doRequest( $function, $parameters );

        $packageStatusResponse = new PackageStatus( $response->SubmitInfoResult );

        $this->connector->log( 'Integration@sendOrder', compact( 'packageStatusResponse' ) );

        return $packageStatusResponse;
    }

    /**
     * @param  \RodrigoPedra\ClearSaleID\Entity\Request\Order $order
     *
     * @return string
     * @throws \RodrigoPedra\ClearSaleID\Exception\RequiredFieldException
     */
    private function createRequestOrderXML( OrderRequest $order )
    {
        $xmlWriter = new XMLWriter;

        $xmlWriter->openMemory();
        $xmlWriter->startDocument( '1.0', 'UTF-8' );

        $order->toXML( $xmlWriter );

        $xmlWriter->endDocument();

        return $xmlWriter->outputMemory( true );
    }

    /**
     * Retorna o status de um pedido
     *
     * @param  string $orderId
     *
     * @return \RodrigoPedra\ClearSaleID\Entity\Response\PackageStatus
     * @throws \RodrigoPedra\ClearSaleID\Exception\UnexpectedErrorException
     */
    public function checkOrderStatus( $orderId )
    {
        $function   = 'CheckOrderStatus';
        $parameters = [
            'entityCode'      => $this->connector->getEntityCode(),
            'pedidoIDCliente' => $orderId
        ];

        $response = $this->connector->doRequest( $function, $parameters );

        $packageStatusResponse = new PackageStatus( $response->CheckOrderStatusResult );

        $this->connector->log( 'Integration@checkOrderStatus', compact( 'packageStatusResponse' ) );

        return $packageStatusResponse;
    }

    /**
     * Método que atualiza o status do pedido para o status recebido no parametro statusPedido
     *
     * @param  string $orderId
     * @param  int    $newStatusId
     * @param  string $notes
     *
     * @return \RodrigoPedra\ClearSaleID\Entity\Response\UpdateOrderStatus
     * @throws \RodrigoPedra\ClearSaleID\Exception\UnexpectedErrorException
     * @throws \RodrigoPedra\ClearSaleID\Exception\UpdateOrderStatusException
     */
    public function updateOrderStatus( $orderId, $newStatusId, $notes = '' )
    {
        $function   = 'UpdateOrderStatus';
        $parameters = [
            'entityCode'  => $this->connector->getEntityCode(),
            'orderId'     => $orderId,
            'newStatusId' => $newStatusId,
            'obs'         => substr( trim( $notes ), 0, 50 ),
        ];

        // false indicates this method uses extended endpoint
        $response = $this->connector->doRequest( $function, $parameters, false );

        $updateOrderStatusResponse = new UpdateOrderStatus( $response->UpdateOrderStatusResult );

        $this->connector->log( 'Integration@updateOrderStatus', compact( 'updateOrderStatusResponse' ) );

        return $updateOrderStatusResponse;
    }
}