"""
@package psmap.instructions

@brief Map feature objects

Classes:
 - dialogs::Instruction
 - dialogs::InstructionObject
 - dialogs::InitMap
 - dialogs::MapFrame
 - dialogs::PageSetup
 - dialogs::Mapinfo
 - dialogs::Text
 - dialogs::Image
 - dialogs::NorthArrow
 - dialogs::Point
 - dialogs::Line
 - dialogs::Rectangle
 - dialogs::Scalebar
 - dialogs::RasterLegend
 - dialogs::VectorLegend
 - dialogs::Raster
 - dialogs::Vector
 - dialogs::VProperties

(C) 2011-2012 by Anna Kratochvilova, and the GRASS Development Team

This program is free software under the GNU General Public License
(>=v2). Read the file COPYING that comes with GRASS for details.

@author Anna Kratochvilova <kratochanna gmail.com> (bachelor's project)
@author Martin Landa <landa.martin gmail.com> (mentor)
"""

import os
import string
import six
from math import ceil
from time import strftime, localtime
from io import open

import wx
import grass.script as grass
from grass.script.task import cmdlist_to_tuple

from core.gcmd import RunCommand, GError, GMessage, GWarning
from core.utils import GetCmdString
from dbmgr.vinfo import VectorDBInfo
from gui_core.wrap import NewId as wxNewId
from psmap.utils import *


def NewId():
    return int(wxNewId())


class Instruction:
    """Class which represents instruction file"""

    def __init__(self, parent, objectsToDraw, env):

        self.parent = parent
        self.objectsToDraw = objectsToDraw
        # here are kept objects like mapinfo, rasterlegend, etc.
        self.instruction = list()
        self.env = env

    def __str__(self):
        """Returns text for instruction file"""
        comment = "# timestamp: " + strftime("%Y-%m-%d %H:%M",
                                             localtime()) + '\n'
        env = grass.gisenv()
        comment += "# location: %s\n" % env['LOCATION_NAME']
        comment += "# mapset: %s\n" % env['MAPSET']
        comment += "# page orientation: %s\n" % self.FindInstructionByType('page')[
            'Orientation']
        border = ''
        if not self.FindInstructionByType('map'):
            border = 'border n\n'
        text = [str(each) for each in self.instruction]
        return comment + border + '\n'.join(text) + '\nend'

    def __getitem__(self, id):
        for each in self.instruction:
            if each.id == id:
                return each
        return None

    def __contains__(self, id):
        """Test if instruction is included"""
        for each in self.instruction:
            if each.id == id:
                return True
        return False

    def __delitem__(self, id):
        """Delete instruction"""
        for each in self.instruction:
            if each.id == id:
                if each.type == 'map':
                    # must remove raster, vector layers, labels too
                    vektor = self.FindInstructionByType('vector', list=True)
                    vProperties = self.FindInstructionByType(
                        'vProperties', list=True)
                    raster = self.FindInstructionByType('raster', list=True)
                    labels = self.FindInstructionByType('labels', list=True)
                    for item in vektor + vProperties + raster + labels:
                        if item in self.instruction:
                            self.instruction.remove(item)

                self.instruction.remove(each)
                if id in self.objectsToDraw:
                    self.objectsToDraw.remove(id)
                return

    def AddInstruction(self, instruction):
        """Add instruction"""
        # add to instructions
        if instruction.type == 'map':
            self.instruction.insert(0, instruction)
        else:
            self.instruction.append(instruction)
        # add to drawable objects
        if instruction.type not in (
                'page', 'raster', 'vector', 'vProperties', 'initMap',
                'labels',
        ):
            if instruction.type == 'map':
                self.objectsToDraw.insert(0, instruction.id)
            else:
                self.objectsToDraw.append(instruction.id)

    def FindInstructionByType(self, type, list=False):
        """Find instruction(s) with the given type"""
        inst = []
        for each in self.instruction:
            if each.type == type:
                inst.append(each)
        if len(inst) == 1 and not list:
            return inst[0]
        return inst

    def Read(self, filename):
        """Reads instruction file and creates instruction objects"""
        self.filename = filename
        # open file
        try:
            file = open(filename, encoding='Latin_1', errors='ignore')
        except IOError:
            GError(message=_("Unable to open file\n%s") % filename)
            return
        # first read file to get information about region and scaletype
        isRegionComment = False
        orientation = 'Portrait'
        for line in file:
            if '# g.region' in line:
                self.SetRegion(regionInstruction=line)
                isRegionComment = True
                break
            if '# page orientation' in line:
                orientation = line.split(':')[-1].strip()

        if not isRegionComment:
            self.SetRegion(regionInstruction=None)
        # then run ps.map -b to get information for maploc
        # compute scale and center
        map = self.FindInstructionByType('map')
        region = grass.region(env=self.env)
        map['center'] = (region['n'] + region['s']
                         ) / 2, (region['w'] + region['e']) / 2
        mapRect = GetMapBounds(
            self.filename, portrait=(
                orientation == 'Portrait'),
            env=self.env)
        map['rect'] = mapRect
        proj = projInfo()
        toM = 1.0
        if proj['units']:
            toM = float(proj['meters'])
        units = UnitConversion(self.parent)
        w = units.convert(
            value=mapRect.Get()[2],
            fromUnit='inch',
            toUnit='meter') / toM
        map['scale'] = w / abs((region['w'] - region['e']))

        SetResolution(
            dpi=300,
            width=map['rect'].width,
            height=map['rect'].height,
            env=self.env)

        # read file again, now with information about map bounds
        isBuffer = False
        buffer = []
        instruction = None
        vectorMapNumber = 1
        file.seek(0)
        for line in file:
            if not line.strip():
                continue
            line = line.strip()
            if isBuffer:
                buffer.append(line)
                if 'end' in line:
                    isBuffer = False
                    kwargs = {}
                    if instruction == 'scalebar':
                        kwargs['scale'] = map['scale']
                    elif instruction in ('text', 'eps', 'point', 'line', 'rectangle'):
                        kwargs['mapInstruction'] = map
                    elif instruction in ('vpoints', 'vlines', 'vareas'):
                        kwargs['id'] = NewId()
                        kwargs['vectorMapNumber'] = vectorMapNumber
                        vectorMapNumber += 1
                    elif instruction == 'paper':
                        kwargs['Orientation'] = orientation

                    ok = self.SendToRead(instruction, buffer, **kwargs)
                    if not ok:
                        return False
                    buffer = []
                continue

            elif line.startswith('paper'):
                instruction = 'paper'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('border'):
                if line.split()[1].lower() in ('n', 'no', 'none'):
                    ok = self.SendToRead('border', [line])
                    if not ok:
                        return False
                elif line.split()[1].lower() in ('y', 'yes'):
                    instruction = 'border'
                    isBuffer = True
                    buffer.append(line)

            elif line.startswith('scale '):
                if isBuffer:
                    continue
                ok = self.SendToRead(
                    'scale', line, isRegionComment=isRegionComment)
                if not ok:
                    return False

            elif line.startswith('maploc'):
                ok = self.SendToRead(instruction='maploc', text=line)
                if not ok:
                    return False

            elif line.startswith('raster'):
                ok = self.SendToRead(instruction='raster', text=line)
                if not ok:
                    return False

            elif line.startswith('mapinfo'):
                instruction = 'mapinfo'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('scalebar'):
                instruction = 'scalebar'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('text'):
                instruction = 'text'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('eps'):
                instruction = 'eps'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('point'):
                instruction = 'point'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('line'):
                instruction = 'line'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('rectangle'):
                instruction = 'rectangle'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('colortable'):
                if len(line.split()) == 2 and line.split()[
                        1].lower() in ('n', 'no', 'none'):
                    break
                instruction = 'colortable'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('vlegend'):
                instruction = 'vlegend'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('vpoints'):
                instruction = 'vpoints'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('vlines'):
                instruction = 'vlines'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('vareas'):
                instruction = 'vareas'
                isBuffer = True
                buffer.append(line)

            elif line.startswith('labels'):
                instruction = 'labels'
                isBuffer = True
                buffer.append(line)

        rasterLegend = self.FindInstructionByType('rasterLegend')
        raster = self.FindInstructionByType('raster')
        page = self.FindInstructionByType('page')
        vector = self.FindInstructionByType('vector')
        vectorLegend = self.FindInstructionByType('vectorLegend')
        vectorMaps = self.FindInstructionByType('vProperties', list=True)

        # check (in case of scaletype 0) if map is drawn also
        map['drawMap'] = False
        if map['scaleType'] == 0:
            mapForRegion = map['map']
            if map['mapType'] == 'raster' and raster:
                if mapForRegion == raster['raster']:
                    map['drawMap'] = True
            elif map['mapType'] == 'vector' and vector:
                for vmap in vector['list']:
                    if mapForRegion == vmap[0]:
                        map['drawMap'] = True

        # rasterLegend
        if rasterLegend:
            if rasterLegend['rasterDefault'] and raster:
                rasterLegend['raster'] = raster['raster']
                if not rasterLegend['discrete']:
                    rasterType = getRasterType(map=rasterLegend['raster'])
                    if rasterType == 'CELL':
                        rasterLegend['discrete'] = 'y'
                    else:
                        rasterLegend['discrete'] = 'n'

            # estimate size
            height = rasterLegend.EstimateHeight(
                raster=rasterLegend['raster'],
                discrete=rasterLegend['discrete'],
                fontsize=rasterLegend['fontsize'],
                cols=rasterLegend['cols'],
                height=rasterLegend['height'])
            width = rasterLegend.EstimateWidth(
                raster=rasterLegend['raster'],
                discrete=rasterLegend['discrete'],
                fontsize=rasterLegend['fontsize'],
                cols=rasterLegend['cols'],
                width=rasterLegend['width'],
                paperInstr=page)
            rasterLegend['rect'] = Rect2D(
                x=float(rasterLegend['where'][0]),
                y=float(rasterLegend['where'][1]),
                width=width, height=height)

        # vectors, vlegend

        if vector:
            for vmap in vectorMaps:
                for i, each in enumerate(vector['list']):
                    if each[2] == vmap.id:

                        vector['list'][i][4] = vmap['label']
                        vector['list'][i][3] = vmap['lpos']
            if vectorLegend:
                size = vectorLegend.EstimateSize(
                    vectorInstr=vector,
                    fontsize=vectorLegend['fontsize'],
                    width=vectorLegend['width'],
                    cols=vectorLegend['cols'])
                vectorLegend['rect'] = Rect2D(
                    x=float(vectorLegend['where'][0]),
                    y=float(vectorLegend['where'][1]),
                    width=size[0],
                    height=size[1])

        page = self.FindInstructionByType('page')
        if not page:
            page = PageSetup(NewId(), env=self.env)
            self.AddInstruction(page)
        else:
            page['Orientation'] = orientation

        #
        return True

    def SendToRead(self, instruction, text, **kwargs):
        psmapInstrDict = dict(paper=['page'],
                              maploc=['map'],
                              scale=['map'],
                              border=['map'],
                              raster=['raster'],
                              mapinfo=['mapinfo'],
                              scalebar=['scalebar'],
                              text=['text'],
                              eps=['image', 'northArrow'],
                              point=['point'],
                              line=['line'],
                              rectangle=['rectangle'],
                              vpoints=['vector', 'vProperties'],
                              vlines=['vector', 'vProperties'],
                              vareas=['vector', 'vProperties'],
                              colortable=['rasterLegend'],
                              vlegend=['vectorLegend'],
                              labels=['labels']
                              )

        myInstrDict = dict(page=PageSetup,
                           map=MapFrame,
                           raster=Raster,
                           mapinfo=Mapinfo,
                           scalebar=Scalebar,
                           text=Text,
                           image=Image,
                           northArrow=NorthArrow,
                           point=Point,
                           line=Line,
                           rectangle=Rectangle,
                           rasterLegend=RasterLegend,
                           vectorLegend=VectorLegend,
                           vector=Vector,
                           vProperties=VProperties,
                           labels=Labels
                           )

        myInstruction = psmapInstrDict[instruction]

        for i in myInstruction:
            instr = self.FindInstructionByType(i)
            if i in ('text', 'vProperties', 'image', 'northArrow',
                     'point', 'line', 'rectangle') or not instr:

                id = NewId()  # !vProperties expect subtype
                if i == 'vProperties':
                    id = kwargs['id']
                    newInstr = myInstrDict[i](
                        id, subType=instruction[1:], env=self.env,
                    )
                elif i in ('image', 'northArrow'):
                    commentFound = False
                    for line in text:
                        if line.find("# north arrow") >= 0:
                            commentFound = True
                    if i == 'image' and commentFound or \
                       i == 'northArrow' and not commentFound:
                        continue
                    newInstr = myInstrDict[i](id, settings=self, env=self.env)
                else:
                    newInstr = myInstrDict[i](id, env=self.env)
                ok = newInstr.Read(instruction, text, **kwargs)
                if ok:
                    self.AddInstruction(newInstr)
                else:
                    return False

            else:
                ok = instr.Read(instruction, text, **kwargs)

                if not ok:
                    return False
        return True

    def SetRegion(self, regionInstruction):
        """Sets region from file comment or sets current region in case of no comment"""
        map = MapFrame(NewId(), env=self.env)
        self.AddInstruction(map)
        if regionInstruction:
            cmd = cmdlist_to_tuple(regionInstruction.strip('# ').split())

            # define scaleType
            if len(cmd[1]) <= 3:
                if 'raster' in cmd[1]:
                    map['scaleType'] = 0
                    map['mapType'] = 'raster'
                    map['map'] = cmd[1]['raster']
                elif 'vector' in cmd[1]:
                    map['scaleType'] = 0
                    map['mapType'] = 'vector'
                    map['map'] = cmd[1]['vector']
                elif 'region' in cmd[1]:
                    map['scaleType'] = 1
                    map['region'] = cmd[1]['region']

            else:
                map['scaleType'] = 2
        else:
            map['scaleType'] = 2
            region = grass.region(env=None)
            cmd = ['g.region', region]
        cmdString = GetCmdString(cmd).replace('g.region', '')
        GMessage(
            _("Instruction file will be loaded with following region: %s\n") %
            cmdString)
        try:
            self.env['GRASS_REGION'] = grass.region_env(env=self.env, **cmd[1])

        except grass.ScriptError as e:
            GError(_("Region cannot be set\n%s") % e)
            return False


class InstructionObject:
    """Abtract class representing single instruction"""

    def __init__(self, id, env):
        self.id = id
        self.env = env

        # default values
        self.defaultInstruction = dict()
        # current values
        self.instruction = self.defaultInstruction
        # converting units
        self.unitConv = UnitConversion()

    def __str__(self):
        """Returns particular part of text instruction"""
        return ''

    def __getitem__(self, key):
        for each in self.instruction.keys():
            if each == key:
                return self.instruction[key]
        return None

    def __setitem__(self, key, value):
        self.instruction[key] = value

    def GetInstruction(self):
        """Get current values"""
        return self.instruction

    def SetInstruction(self, instruction):
        """Set default values"""
        self.instruction = instruction

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save them"""
        pass

    def PercentToReal(self, e, n):
        """Converts text coordinates from percent of region to map coordinates"""
        e, n = float(e.strip('%')), float(n.strip('%'))
        region = grass.region(env=self.env)
        N = region['s'] + (region['n'] - region['s']) / 100 * n
        E = region['w'] + (region['e'] - region['w']) / 100 * e
        return E, N


class InitMap(InstructionObject):
    """Class representing virtual map"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'initMap'

        # default values
        self.defaultInstruction = dict(rect=None, scale=None)
        # current values
        self.instruction = dict(self.defaultInstruction)


class MapFrame(InstructionObject):
    """Class representing map (instructions maploc, scale, border)"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'map'
        # default values
        self.defaultInstruction = dict(
            map=None,
            mapType=None,
            drawMap=True,
            region=None,
            rect=Rect2D(),
            scaleType=0,
            scale=None,
            center=None,
            resolution=300,
            border='y',
            width=1,
            color='0:0:0')
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        instr = ''
        comment = ''

        # region settings
        region = grass.region(env=self.env)
        if self.instruction['scaleType'] == 0:  # match map
            map = self.instruction['map']
            if self.instruction['mapType'] == 'raster':
                comment = "# g.region raster=%s nsres=%s ewres=%s\n" % (
                    map, region['nsres'], region['ewres'])
            else:
                comment = "# g.region vector=%s\n" % (map)
        elif self.instruction['scaleType'] == 1:  # saved region
            region = self.instruction['region']
            comment = "# g.region region=%s\n" % region
        # current region, fixed scale
        elif self.instruction['scaleType'] in (2, 3):
            comment = string.Template(
                "# g.region n=$n s=$s e=$e w=$w rows=$rows cols=$cols \n").substitute(**region)

        instr += comment
        instr += '\n'
        # maploc
        maplocInstruction = "maploc %.3f %.3f" % (
            self.instruction['rect'].x, self.instruction['rect'].y)
        if self.instruction['scaleType'] != 3:
            maplocInstruction += "  %.3f %.3f" % (
                self.instruction['rect'].width, self.instruction['rect'].height)
        instr += maplocInstruction
        instr += '\n'

        # scale
        if self.instruction['scaleType'] == 3:  # fixed scale
            scaleInstruction = "scale 1:%.0f" % (1 / self.instruction['scale'])
            instr += scaleInstruction
            instr += '\n'
        # border
        borderInstruction = ''
        if self.instruction['border'] == 'n':
            borderInstruction = "border n"
        else:
            borderInstruction = "border y\n"
            borderInstruction += string.Template(
                "    width $width\n    color $color\n").substitute(
                self.instruction)
            borderInstruction += "    end"
        instr += borderInstruction
        instr += '\n'

        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        if 'isRegionComment' in kwargs:
            isRegionComment = kwargs['isRegionComment']
        instr = {}

        if instruction == 'border':
            for line in text:
                if line.startswith('end'):
                    break
                try:
                    if line.split()[1].lower() in ('n', 'no', 'none'):
                        instr['border'] = 'n'
                        break
                    elif line.split()[1].lower() in ('y', 'yes'):
                        instr['border'] = 'y'
                    elif line.startswith('width'):
                        instr['width'] = line.split()[1]
                    elif line.startswith('color'):
                        instr['color'] = line.split()[1]
                except IndexError:
                    GError(_("Failed to read instruction %s") % instruction)
                    return False

        elif instruction == 'scale':
            try:
                scaleText = text.strip('scale ').split(':')[1]
                # when scale instruction given and region comment also, then
                # scaletype is fixed scale
                if not isRegionComment:
                    instr['scaleType'] = 2
                else:
                    instr['scaleType'] = 3

                scale = 1 / float(scaleText)
                if abs(scale - self.instruction['scale']) > (0.01 * scale):
                    GWarning(_("Scale has changed, old value: %(old)s\nnew value: %(new)s") %
                             {'old': scale, 'new': self.instruction['scale']})
            except (ValueError, IndexError):
                GError(
                    _("Failed to read instruction %s.\nUse 1:25000 notation.") %
                    instruction)
                return False

        elif instruction == 'maploc':
            maploc = text.strip('maploc ').split()
            if len(maploc) >= 2:
                if  abs(self.instruction['rect'].Get()[0] - float(maploc[0])) > 0.5 or \
                        abs(self.instruction['rect'].Get()[1] - float(maploc[1])) > 0.5:
                    GWarning(
                        _("Map frame position changed, old value: %(old1)s %(old2)s\nnew value: %(new1)s %(new2)s") % {
                            'old1': maploc[0],
                            'old2': maploc[1],
                            'new1': self.instruction['rect'].Get()[0],
                            'new2': self.instruction['rect'].Get()[1]})

                #instr['rect'] = wx.Rect2D(float(maploc[0]), float(maploc[1]), self.instruction['rect'][2], self.instruction['rect'][3])
            if len(maploc) == 4:
                if  abs(self.instruction['rect'].Get()[2] - float(maploc[2])) > 0.5 or \
                        abs(self.instruction['rect'].Get()[3] - float(maploc[3])) > 0.5:
                    GWarning(
                        _("Map frame size changed, old value: %(old1)s %(old2)s\nnew value: %(new1)s %(new2)s") % {
                            'old1': maploc[2],
                            'old2': maploc[3],
                            'new1': self.instruction['rect'].Get()[2],
                            'new2': self.instruction['rect'].Get()[3]})
                #instr['rect'] = wx.Rect2D(*map(float, maploc))
        self.instruction.update(instr)
        return True


class PageSetup(InstructionObject):
    """Class representing page instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'page'
        # default values
        self.defaultInstruction = dict(
            Units='inch',
            Format='a4',
            Orientation='Portrait',
            Width=8.268,
            Height=11.693,
            Left=0.5,
            Right=0.5,
            Top=1,
            Bottom=1)
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        if self.instruction['Format'] == 'custom':
            instr = string.Template(
                "paper\n    width $Width\n    height $Height\n").substitute(
                self.instruction)
        else:
            instr = string.Template(
                "paper $Format\n").substitute(self.instruction)
        instr += string.Template(
            "    left $Left\n    right $Right\n    bottom $Bottom\n    top $Top\n    end").substitute(self.instruction)

        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        instr = {}
        self.cats = ['Width', 'Height', 'Left', 'Right', 'Top', 'Bottom']
        self.subInstr = dict(
            zip(['width', 'height', 'left', 'right', 'top', 'bottom'], self.cats))

        if instruction == 'paper':  # just for sure
            for line in text:
                if line.startswith('paper'):
                    if len(line.split()) > 1:
                        pformat = line.split()[1]
                        availableFormats = self._toDict(
                            grass.read_command(
                                'ps.map', flags='p', quiet=True))
                        # e.g. paper a3
                        try:
                            instr['Format'] = pformat
                            for key, value in six.iteritems(availableFormats[
                                                            pformat]):
                                instr[key] = float(value)
                            break
                        except KeyError:
                            GError(
                                _("Failed to read instruction %(file)s.\nUnknown format %(for)s") % {
                                    'file': instruction,
                                    'for': format})
                            return False
                    else:
                        # paper
                        # width ...
                        instr['Format'] = 'custom'
                # read subinstructions
                elif instr['Format'] == 'custom' and not line.startswith('end'):
                    text = line.split()
                    try:
                        instr[self.subInstr[text[0]]] = float(text[1])
                    except (IndexError, KeyError):
                        GError(
                            _("Failed to read instruction %s.") %
                            instruction)
                        return False

            if 'Orientation' in kwargs and kwargs[
                    'Orientation'] == 'Landscape':
                instr['Width'], instr['Height'] = instr[
                    'Height'], instr['Width']

            self.instruction.update(instr)
        return True

    def _toDict(self, paperStr):
        sizeDict = dict()
#     cats = self.subInstr[ 'Width', 'Height', 'Left', 'Right', 'Top', 'Bottom']
        for line in paperStr.strip().split('\n'):
            d = dict(zip(self.cats, line.split()[1:]))
            sizeDict[line.split()[0]] = d

        return sizeDict


class Mapinfo(InstructionObject):
    """Class representing mapinfo instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'mapinfo'
        # default values
        self.defaultInstruction = dict(
            unit='inch',
            where=(
                0,
                0),
            font='Helvetica',
            fontsize=10,
            color='0:0:0',
            background='none',
            border='none',
            rect=None)
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        instr = "mapinfo\n"
        instr += "    where %.3f %.3f\n" % (
            self.instruction['where'][0],
            self.instruction['where'][1])
        instr += string.Template(
            "    font $font\n    fontsize $fontsize\n    color $color\n").substitute(self.instruction)
        instr += string.Template(
            "    background $background\n    border $border\n").substitute(
            self.instruction)
        instr += "    end"
        return instr

    def Read(self, instruction, text):
        """Read instruction and save information"""
        instr = {}
        try:
            for line in text:
                sub = line.split(None, 1)
                if sub[0] == 'font':
                    instr['font'] = sub[1]
                elif sub[0] == 'fontsize':
                    instr['fontsize'] = int(sub[1])
                elif sub[0] == 'color':
                    instr['color'] = sub[1]
                elif sub[0] == 'background':
                    instr['background'] = sub[1]
                elif sub[0] == 'border':
                    instr['border'] = sub[1]
                elif sub[0] == 'where':
                    instr['where'] = float(
                        sub[1].split()[0]), float(
                        sub[1].split()[1])
        except (ValueError, IndexError):
            GError(_("Failed to read instruction %s") % instruction)
            return False
        self.instruction.update(instr)
        self.instruction['rect'] = self.EstimateRect(
            mapinfoDict=self.instruction)
        return True

    def EstimateRect(self, mapinfoDict):
        """Estimate size to draw mapinfo"""
        w = mapinfoDict['fontsize'] * 20  # any better estimation?
        h = mapinfoDict['fontsize'] * 7
        width = self.unitConv.convert(value=w, fromUnit='point', toUnit='inch')
        height = self.unitConv.convert(
            value=h, fromUnit='point', toUnit='inch')
        return Rect2D(x=float(mapinfoDict['where'][0]), y=float(
            mapinfoDict['where'][1]), width=width, height=height)


class Text(InstructionObject):
    """Class representing text instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'text'
        # default values
        self.defaultInstruction = dict(
            text="", font="Helvetica", fontsize=10, color='black',
            background='none', hcolor='none', hwidth=1, border='none',
            width='1', XY=True, where=(0, 0),
            unit='inch', rotate=None, ref="center center", xoffset=0,
            yoffset=0, east=None, north=None)
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        text = self.instruction['text'].replace('\n', '\\n')
        instr = u"text %s %s" % (
            self.instruction['east'],
            self.instruction['north'])
        instr += " %s\n" % text
        instr += (
            string.Template("    font $font\n    fontsize $fontsize\n    color $color\n"). substitute(
                self.instruction))
        instr += string.Template("    hcolor $hcolor\n").substitute(self.instruction)
        if self.instruction['hcolor'] != 'none':
            instr += string.Template(
                "    hwidth $hwidth\n").substitute(self.instruction)
        instr += string.Template("    border $border\n").substitute(self.instruction)
        if self.instruction['border'] != 'none':
            instr += string.Template(
                "    width $width\n").substitute(self.instruction)
        instr += string.Template(
            "    background $background\n").substitute(self.instruction)
        if self.instruction["ref"] != '0':
            instr += string.Template("    ref $ref\n").substitute(self.instruction)
        if self.instruction["rotate"]:
            instr += string.Template(
                "    rotate $rotate\n").substitute(self.instruction)
        if float(
                self.instruction["xoffset"]) or float(
                self.instruction["yoffset"]):
            instr += (
                string.Template("    xoffset $xoffset\n    yoffset $yoffset\n"). substitute(
                    self.instruction))
        instr += "    end"
        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        map = kwargs['mapInstruction']
        instr = {}
        for line in text:
            try:
                sub = line.split(None, 1)[0]
                if sub == 'text':
                    e, n = line.split(None, 3)[1:3]
                    if '%' in e and '%' in n:
                        instr['XY'] = True
                        instr['east'], instr[
                            'north'] = self.PercentToReal(e, n)
                    else:
                        instr['XY'] = False
                        instr['east'], instr['north'] = float(e), float(n)

                    instr['text'] = line.split(None, 3)[3]

                elif sub == 'font':
                    instr['font'] = line.split(None, 1)[1]
                elif sub == 'fontsize':
                    instr['fontsize'] = float(line.split(None, 1)[1])
                elif sub == 'color':
                    instr['color'] = line.split(None, 1)[1]
                elif sub == 'width':
                    instr['width'] = line.split(None, 1)[1]
                elif sub == 'hcolor':
                    instr['hcolor'] = line.split(None, 1)[1]
                elif sub == 'hwidth':
                    instr['hwidth'] = line.split(None, 1)[1]
                elif sub == 'background':
                    instr['background'] = line.split(None, 1)[1]
                elif sub == 'border':
                    instr['border'] = line.split(None, 1)[1]
                elif sub == 'ref':
                    instr['ref'] = line.split(None, 1)[1]
                elif sub == 'rotate':
                    instr['rotate'] = float(line.split(None, 1)[1])
                elif sub == 'xoffset':
                    instr['xoffset'] = int(line.split(None, 1)[1])
                elif sub == 'yoffset':
                    instr['yoffset'] = int(line.split(None, 1)[1])
                elif sub == 'opaque':
                    if line.split(None, 1)[1].lower() in ('n', 'none'):
                        instr['background'] = 'none'

            except(IndexError, ValueError):
                GError(_("Failed to read instruction %s") % instruction)
                return False
        instr['where'] = PaperMapCoordinates(
            mapInstr=map, x=instr['east'],
            y=instr['north'],
            paperToMap=False,
            env=self.env)
        self.instruction.update(instr)

        return True


class Image(InstructionObject):
    """Class representing eps instruction - image"""

    def __init__(self, id, settings, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.settings = settings
        self.type = 'image'
        # default values
        self.defaultInstruction = dict(
            epsfile="",
            XY=True,
            where=(
                0,
                0),
            unit='inch',
            east=None,
            north=None,
            rotate=None,
            scale=1)
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        self.ChangeRefPoint(toCenter=True)
        epsfile = self.instruction['epsfile'].replace(
            os.getenv('GISBASE'), "$GISBASE")

        instr = "eps %s %s\n" % (
            self.instruction['east'],
            self.instruction['north'])
        instr += "    epsfile %s\n" % epsfile
        if self.instruction["rotate"]:
            instr += string.Template(
                "    rotate $rotate\n").substitute(self.instruction)
        if self.instruction["scale"]:
            instr += string.Template(
                "    scale $scale\n").substitute(self.instruction)
        instr += "    end"
        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        mapInstr = kwargs['mapInstruction']
        instr = {}
        for line in text:
            try:
                sub = line.split(None, 1)[0]
                if sub == 'eps':
                    e, n = line.split(None, 3)[1:3]
                    if '%' in e and '%' in n:
                        instr['XY'] = True
                        instr['east'], instr[
                            'north'] = self.PercentToReal(e, n)
                    else:
                        instr['XY'] = False
                        instr['east'], instr['north'] = float(e), float(n)

                elif sub == 'epsfile':
                    epsfile = line.split(None, 1)[1]
                    instr['epsfile'] = epsfile.replace(
                        "$GISBASE", os.getenv("GISBASE"))
                elif sub == 'rotate':
                    instr['rotate'] = float(line.split(None, 1)[1])
                elif sub == 'scale':
                    instr['scale'] = float(line.split(None, 1)[1])

            except(IndexError, ValueError):
                GError(_("Failed to read instruction %s") % instruction)
                return False
        if not os.path.exists(instr['epsfile']):
            GError(_("Failed to read instruction %(inst)s: "
                     "file %(file)s not found.") % {'inst': instruction,
                                                    'file': instr['epsfile']})
            return False

        instr['epsfile'] = os.path.abspath(instr['epsfile'])
        instr['size'] = self.GetImageOrigSize(instr['epsfile'])
        if 'rotate' in instr:
            instr['size'] = BBoxAfterRotation(
                instr['size'][0], instr['size'][1], instr['rotate'])
        self.instruction.update(instr)
        self.ChangeRefPoint(toCenter=False)
        instr['where'] = PaperMapCoordinates(
            mapInstr=mapInstr,
            x=self.instruction['east'],
            y=self.instruction['north'],
            paperToMap=False,
            env=self.env)
        w = self.unitConv.convert(
            value=instr['size'][0],
            fromUnit='point', toUnit='inch')
        h = self.unitConv.convert(
            value=instr['size'][1],
            fromUnit='point', toUnit='inch')
        instr['rect'] = Rect2D(
            x=float(instr['where'][0]),
            y=float(instr['where'][1]),
            width=w * self.instruction['scale'],
            height=h * self.instruction['scale'])
        self.instruction.update(instr)

        return True

    def ChangeRefPoint(self, toCenter):
        """Change reference point (left top x center)"""
        mapInstr = self.settings.FindInstructionByType('map')
        if not mapInstr:
            mapInstr = self.settings.FindInstructionByType('initMap')
        mapId = mapInstr.id
        if toCenter:
            center = self.instruction['rect'].GetCentre()
            ENCenter = PaperMapCoordinates(
                mapInstr=self.settings[mapId],
                x=center[0],
                y=center[1],
                paperToMap=True,
                env=self.env)

            self.instruction['east'], self.instruction['north'] = ENCenter
        else:
            x, y = PaperMapCoordinates(
                mapInstr=self.settings[mapId],
                x=self.instruction['east'],
                y=self.instruction['north'],
                paperToMap=False,
                env=self.env)
            w = self.unitConv.convert(
                value=self.instruction['size'][0],
                fromUnit='point', toUnit='inch')
            h = self.unitConv.convert(
                value=self.instruction['size'][1],
                fromUnit='point', toUnit='inch')
            x -= w * self.instruction['scale'] / 2
            y -= h * self.instruction['scale'] / 2
            e, n = PaperMapCoordinates(
                mapInstr=self.settings[mapId],
                x=x, y=y, paperToMap=True,
                env=self.env)
            self.instruction['east'], self.instruction['north'] = e, n

    def GetImageOrigSize(self, imagePath):
        """Get image size.

        If eps, size is read from image header.
        """
        fileName = os.path.split(imagePath)[1]
        # if eps, read info from header
        if os.path.splitext(fileName)[1].lower() == '.eps':
            bbInfo = "%%BoundingBox"
            file = open(imagePath, "r")
            w = h = 0
            while file:
                line = file.readline()
                if line.find(bbInfo) == 0:
                    w, h = line.split()[3:5]
                    break
            file.close()
            return float(w), float(h)
        else:  # we can use wx.Image
            img = wx.Image(fileName, type=wx.BITMAP_TYPE_ANY)
            return img.GetWidth(), img.GetHeight()


class NorthArrow(Image):
    """Class representing eps instruction -- North Arrow"""

    def __init__(self, id, settings, env):
        Image.__init__(self, id=id, settings=settings, env=env)
        self.type = 'northArrow'

    def __str__(self):
        self.ChangeRefPoint(toCenter=True)
        epsfile = self.instruction['epsfile'].replace(
            os.getenv('GISBASE'), "$GISBASE")

        instr = "eps %s %s\n" % (
            self.instruction['east'],
            self.instruction['north'])
        instr += "# north arrow\n"
        instr += "    epsfile %s\n" % epsfile
        if self.instruction["rotate"]:
            instr += string.Template(
                "    rotate $rotate\n").substitute(self.instruction)
        if self.instruction["scale"]:
            instr += string.Template(
                "    scale $scale\n").substitute(self.instruction)
        instr += "    end"
        return instr


class Point(InstructionObject):
    """Class representing point instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'point'
        # default values
        self.defaultInstruction = dict(symbol=os.path.join('basic', 'x'),
                                       color='0:0:0', fcolor='200:200:200',
                                       rotate=0, size=10,
                                       XY=True, where=(0, 0), unit='inch',
                                       east=None, north=None)
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        instr = string.Template(
            "point $east $north\n").substitute(self.instruction)
        instr += string.Template("    symbol $symbol\n").substitute(self.instruction)
        instr += string.Template("    color $color\n").substitute(self.instruction)
        instr += string.Template("    fcolor $fcolor\n").substitute(self.instruction)
        instr += string.Template("    rotate $rotate\n").substitute(self.instruction)
        instr += string.Template("    size $size\n").substitute(self.instruction)
        instr += "    end"
        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        mapInstr = kwargs['mapInstruction']
        instr = {}
        for line in text:
            try:
                sub = line.split(None, 1)[0]
                if sub == 'point':
                    e, n = line.split(None, 3)[1:3]
                    if '%' in e and '%' in n:
                        instr['XY'] = True
                        instr['east'], instr[
                            'north'] = self.PercentToReal(e, n)
                    else:
                        instr['XY'] = False
                        instr['east'], instr['north'] = float(e), float(n)

                elif sub == 'symbol':
                    instr['symbol'] = line.split(None, 1)[1]
                elif sub == 'rotate':
                    instr['rotate'] = float(line.split(None, 1)[1])
                elif sub == 'size':
                    instr['size'] = float(line.split(None, 1)[1])
                elif sub == 'color':
                    instr['color'] = line.split(None, 1)[1]
                elif sub == 'fcolor':
                    instr['fcolor'] = line.split(None, 1)[1]

            except(IndexError, ValueError):
                GError(_("Failed to read instruction %s") % instruction)
                return False

        self.instruction.update(instr)
        instr['where'] = PaperMapCoordinates(
            mapInstr=mapInstr,
            x=self.instruction['east'],
            y=self.instruction['north'],
            paperToMap=False,
            env=self.env)
        w = h = self.unitConv.convert(
            value=instr['size'],
            fromUnit='point', toUnit='inch')
        instr['rect'] = Rect2D(
            x=float(instr['where'][0]) - w / 2,
            y=float(instr['where'][1] - h / 2),
            width=w, height=h)
        self.instruction.update(instr)

        return True


class Line(InstructionObject):
    """Class representing line instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'line'
        # default values
        self.defaultInstruction = dict(color='0:0:0', width=2,
                                       where=[wx.Point2D(), wx.Point2D()],
                                       east1=None, north1=None,
                                       east2=None, north2=None)
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        instr = string.Template(
            "line $east1 $north1 $east2 $north2\n").substitute(self.instruction)
        instr += string.Template("    color $color\n").substitute(self.instruction)
        instr += string.Template("    width $width\n").substitute(self.instruction)
        instr += "    end\n"
        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        mapInstr = kwargs['mapInstruction']
        instr = {}
        for line in text:
            try:
                sub = line.split(None, 1)[0]
                if sub == 'line':
                    e1, n1, e2, n2 = line.split(None, 5)[1:5]
                    if '%' in e1 and '%' in n1 and '%' in e2 and '%' in n2:
                        instr['east1'], instr[
                            'north1'] = self.PercentToReal(e1, n1)
                        instr['east2'], instr[
                            'north2'] = self.PercentToReal(e2, n2)
                    else:
                        instr['east1'], instr['north1'] = float(e1), float(n1)
                        instr['east2'], instr['north2'] = float(e2), float(n2)

                elif sub == 'width':
                    instr['width'] = float(line.split(None, 1)[1])
                elif sub == 'color':
                    instr['color'] = line.split(None, 1)[1]

            except(IndexError, ValueError):
                GError(_("Failed to read instruction %s") % instruction)
                return False

        self.instruction.update(instr)
        e1, n1 = PaperMapCoordinates(
            mapInstr=mapInstr, x=self.instruction['east1'],
            y=self.instruction['north1'],
            paperToMap=False,
            env=self.env)
        e2, n2 = PaperMapCoordinates(
            mapInstr=mapInstr, x=self.instruction['east2'],
            y=self.instruction['north2'],
            paperToMap=False,
            env=self.env)
        instr['where'] = [wx.Point2D(e1, n1), wx.Point2D(e2, n2)]
        instr['rect'] = Rect2DPP(instr['where'][0], instr['where'][1])
        self.instruction.update(instr)

        return True


class Rectangle(InstructionObject):
    """Class representing rectangle instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'rectangle'
        # default values
        self.defaultInstruction = dict(color='0:0:0', fcolor='none', width=2,
                                       east1=None, north1=None,
                                       east2=None, north2=None)
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        instr = string.Template(
            "rectangle $east1 $north1 $east2 $north2\n").substitute(self.instruction)
        instr += string.Template("    color $color\n").substitute(self.instruction)
        instr += string.Template("    fcolor $fcolor\n").substitute(self.instruction)
        instr += string.Template("    width $width\n").substitute(self.instruction)
        instr += "    end\n"
        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        mapInstr = kwargs['mapInstruction']
        instr = {}
        for line in text:
            try:
                sub = line.split(None, 1)[0]
                if sub == 'rectangle':
                    e1, n1, e2, n2 = line.split(None, 5)[1:5]
                    if '%' in e1 and '%' in n1 and '%' in e2 and '%' in n2:
                        instr['east1'], instr[
                            'north1'] = self.PercentToReal(e1, n1)
                        instr['east2'], instr[
                            'north2'] = self.PercentToReal(e2, n2)
                    else:
                        instr['east1'], instr['north1'] = float(e1), float(n1)
                        instr['east2'], instr['north2'] = float(e2), float(n2)

                elif sub == 'width':
                    instr['width'] = float(line.split(None, 1)[1])
                elif sub == 'color':
                    instr['color'] = line.split(None, 1)[1]
                elif sub == 'fcolor':
                    instr['fcolor'] = line.split(None, 1)[1]

            except(IndexError, ValueError):
                GError(_("Failed to read instruction %s") % instruction)
                return False

        self.instruction.update(instr)
        e1, n1 = PaperMapCoordinates(
            mapInstr=mapInstr, x=self.instruction['east1'],
            y=self.instruction['north1'],
            paperToMap=False,
            env=self.env)
        e2, n2 = PaperMapCoordinates(
            mapInstr=mapInstr, x=self.instruction['east2'],
            y=self.instruction['north2'],
            paperToMap=False,
            env=self.env)
        instr['rect'] = Rect2DPP(wx.Point2D(e1, n1), wx.Point2D(e2, n2))
        self.instruction.update(instr)

        return True


class Scalebar(InstructionObject):
    """Class representing scalebar instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'scalebar'
        # default values
        self.defaultInstruction = dict(unit='inch', where=(1, 1),
                                       unitsLength='auto', unitsHeight='inch',
                                       length=None, height=0.1, rect=None,
                                       fontsize=10, background='y',
                                       scalebar='f', segment=4, numbers=1)
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        instr = string.Template(
            "scalebar $scalebar\n").substitute(self.instruction)
        instr += "    where %.3f %.3f\n" % (
            self.instruction['where'][0],
            self.instruction['where'][1])
        instr += string.Template(
            "    length $length\n    units $unitsLength\n").substitute(self.instruction)
        instr += string.Template("    height $height\n").substitute(self.instruction)
        instr += string.Template(
            "    segment $segment\n    numbers $numbers\n").substitute(self.instruction)
        instr += string.Template(
            "    fontsize $fontsize\n    background $background\n").substitute(
            self.instruction)
        instr += "    end"
        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        scale = kwargs['scale']
        instr = {}
        for line in text:
            try:
                if line.startswith('scalebar'):
                    if 'scalebar s' in line:
                        instr['scalebar'] = 's'
                    else:
                        instr['scalebar'] = 'f'
                elif line.startswith('where'):
                    instr['where'] = list(map(float, line.split()[1:3]))
                elif line.startswith('length'):
                    instr['length'] = float(line.split()[1])
                elif line.startswith('units'):
                    if line.split()[1] in[
                            'auto', 'meters', 'kilometers', 'feet', 'miles',
                            'nautmiles']:
                        instr['unitsLength'] = line.split()[1]
                elif line.startswith('height'):
                    instr['height'] = float(line.split()[1])
                elif line.startswith('fontsize'):
                    instr['fontsize'] = float(line.split()[1])
                elif line.startswith('numbers'):
                    instr['numbers'] = int(line.split()[1])
                elif line.startswith('segment'):
                    instr['segment'] = int(line.split()[1])
                elif line.startswith('background'):
                    if line.split()[1].strip().lower() in ('y', 'yes'):
                        instr['background'] = 'y'
                    elif line.split()[1].strip().lower() in ('n', 'no', 'none'):
                        instr['background'] = 'n'
            except(IndexError, ValueError):
                GError(_("Failed to read instruction %s") % instruction)
                return False

        self.instruction.update(instr)
        w, h = self.EstimateSize(scalebarDict=self.instruction, scale=scale)
        x = self.instruction['where'][0] - w / 2
        y = self.instruction['where'][1] - h / 2
        self.instruction['rect'] = Rect2D(x, y, w, h)
        return True

    def EstimateSize(self, scalebarDict, scale):
        """Estimate size to draw scalebar"""
        units = projInfo()['units']
        if not units or units not in self.unitConv.getAllUnits():
            units = 'meters'
        if scalebarDict['unitsLength'] != 'auto':
            length = self.unitConv.convert(
                value=scalebarDict['length'],
                fromUnit=scalebarDict['unitsLength'],
                toUnit='inch')
        else:
            length = self.unitConv.convert(
                value=scalebarDict['length'],
                fromUnit=units, toUnit='inch')

        length *= scale
        length *= 1.1  # for numbers on the edge
        height = scalebarDict['height'] + 2 * self.unitConv.convert(
            value=scalebarDict['fontsize'], fromUnit='point', toUnit='inch')
        return (length, height)


class RasterLegend(InstructionObject):
    """Class representing colortable instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'rasterLegend'
        # default values
        self.defaultInstruction = dict(rLegend=False, unit='inch', rasterDefault=True, raster=None,
                                       discrete=None, type=None,
                                       where=(0, 0),
                                       width=None, height=None, cols=1, font="Helvetica", fontsize=10,
                                       # color = '0:0:0', tickbar = False,
                                       # range = False, min = 0, max = 0,
                                       color='black', tickbar='n', range=False, min=0, max=0,
                                       nodata='n')
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        instr = "colortable y\n"
        instr += string.Template("    raster $raster\n").substitute(self.instruction)
        instr += "    where %.3f %.3f\n" % (
            self.instruction['where'][0],
            self.instruction['where'][1])
        if self.instruction['width']:
            instr += string.Template(
                "    width $width\n").substitute(self.instruction)
        instr += string.Template(
            "    discrete $discrete\n").substitute(self.instruction)
        if self.instruction['discrete'] == 'n':
            if self.instruction['height']:
                instr += string.Template(
                    "    height $height\n").substitute(self.instruction)
            instr += string.Template(
                "    tickbar $tickbar\n").substitute(self.instruction)
            if self.instruction['range']:
                instr += string.Template(
                    "    range $min $max\n").substitute(self.instruction)
        else:
            instr += string.Template("    cols $cols\n").substitute(self.instruction)
            instr += string.Template(
                "    nodata $nodata\n").substitute(self.instruction)
        instr += string.Template("    font $font\n    fontsize $fontsize\n    color $color\n")\
            .substitute(self.instruction)
        instr += "    end"
        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        instr = {}
        instr['rLegend'] = True
        for line in text:
            try:
                if line.startswith('where'):
                    instr['where'] = list(map(float, line.split()[1:3]))
                elif line.startswith('font '):
                    instr['font'] = line.split()[1]
                elif line.startswith('fontsize'):
                    instr['fontsize'] = float(line.split()[1])
                elif line.startswith('color '):
                    instr['color'] = line.split()[1]
                elif line.startswith('raster'):
                    instr['raster'] = line.split()[1]
                elif line.startswith('width'):
                    instr['width'] = float(line.split()[1])
                elif line.startswith('height'):
                    instr['height'] = float(line.split()[1])
                elif line.startswith('cols'):
                    instr['cols'] = int(line.split()[1])
                elif line.startswith('range'):
                    instr['range'] = True
                    instr['min'] = float(line.split()[1])
                    instr['max'] = float(line.split()[2])
                elif line.startswith('nodata'):
                    if line.split()[1].strip().lower() in ('y', 'yes'):
                        instr['nodata'] = 'y'
                    elif line.split()[1].strip().lower() in ('n', 'no', 'none'):
                        instr['nodata'] = 'n'
                elif line.startswith('tickbar'):
                    if line.split()[1].strip().lower() in ('y', 'yes'):
                        instr['tickbar'] = 'y'
                    elif line.split()[1].strip().lower() in ('n', 'no', 'none'):
                        instr['tickbar'] = 'n'
                elif line.startswith('discrete'):
                    if line.split()[1].strip().lower() in ('y', 'yes'):
                        instr['discrete'] = 'y'
                    elif line.split()[1].strip().lower() in ('n', 'no', 'none'):
                        instr['discrete'] = 'n'

            except(IndexError, ValueError):
                GError(_("Failed to read instruction %s") % instruction)
                return False

        if 'raster' in instr:
            instr['rasterDefault'] = False
            if 'discrete' not in instr:
                rasterType = getRasterType(map=instr['raster'])
                instr['type'] = rasterType
                if rasterType == 'CELL':
                    instr['discrete'] = 'y'
                else:
                    instr['discrete'] = 'n'

        else:
            instr['rasterDefault'] = True
        self.instruction.update(instr)
        # add 'rect' in the end

        return True

    def EstimateHeight(self, raster, discrete, fontsize,
                       cols=None, height=None):
        """Estimate height to draw raster legend"""
        if discrete == 'n':
            if height:
                height = height
            else:
                height = self.unitConv.convert(value=fontsize * 10,
                                               fromUnit='point', toUnit='inch')

        if discrete == 'y':
            if cols:
                cols = cols
            else:
                cols = 1

            rinfo = grass.raster_info(raster)
            if rinfo['datatype'] in ('DCELL', 'FCELL'):
                minim, maxim = rinfo['min'], rinfo['max']
                rows = ceil(maxim / cols)
            else:
                cat = grass.read_command('r.category', map=raster,
                                         sep=':').strip().split('\n')
                rows = ceil(float(len(cat)) / cols)

            height = self.unitConv.convert(
                value=1.5 * rows * fontsize,
                fromUnit='point',
                toUnit='inch')

        return height

    def EstimateWidth(self, raster, discrete, fontsize,
                      cols=None, width=None, paperInstr=None):
        """Estimate size to draw raster legend"""

        if discrete == 'n':
            rinfo = grass.raster_info(raster)
            minim, maxim = rinfo['min'], rinfo['max']
            if width:
                width = width
            else:
                width = self.unitConv.convert(value=fontsize * 2,
                                              fromUnit='point', toUnit='inch')
            text = len(max(str(minim), str(maxim), key=len))
            textPart = self.unitConv.convert(value=text * fontsize / 2,
                                             fromUnit='point', toUnit='inch')
            width += textPart

        elif discrete == 'y':
            if cols:
                cols = cols
            else:
                cols = 1

            if width:
                width = width
            else:
                paperWidth = paperInstr[
                    'Width'] - paperInstr['Right'] - paperInstr['Left']
                width = (paperWidth / cols) * (cols - 1) + 1

        return width


class VectorLegend(InstructionObject):
    """Class representing colortable instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'vectorLegend'
        # default values
        self.defaultInstruction = dict(
            vLegend=False,
            unit='inch',
            where=(
                0,
                0),
            defaultSize=True,
            width=0.4,
            cols=1,
            span=None,
            font="Helvetica",
            fontsize=10,
            border='none')
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        instr = "vlegend\n"
        instr += "    where %.3f %.3f\n" % (
            self.instruction['where'][0],
            self.instruction['where'][1])
        instr += string.Template(
            "    font $font\n    fontsize $fontsize\n").substitute(self.instruction)
        instr += string.Template(
            "    width $width\n    cols $cols\n").substitute(self.instruction)
        if self.instruction['span']:
            instr += string.Template("    span $span\n").substitute(self.instruction)
        instr += string.Template("    border $border\n").substitute(self.instruction)
        instr += "    end"
        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        instr = {}
        instr['vLegend'] = True
        for line in text:
            try:
                if line.startswith('where'):
                    instr['where'] = list(map(float, line.split()[1:3]))
                elif line.startswith('font '):
                    instr['font'] = line.split()[1]
                elif line.startswith('fontsize'):
                    instr['fontsize'] = float(line.split()[1])
                elif line.startswith('width'):
                    instr['width'] = float(line.split()[1])
                elif line.startswith('cols'):
                    instr['cols'] = int(line.split()[1])
                elif line.startswith('span'):
                    instr['span'] = float(line.split()[1])
                elif line.startswith('border'):
                    instr['border'] = line.split()[1]

            except(IndexError, ValueError):
                GError(_("Failed to read instruction %s") % instruction)
                return False

        self.instruction.update(instr)

        return True

    def EstimateSize(self, vectorInstr, fontsize, width=None, cols=None):
        """Estimate size to draw vector legend"""
        if width:
            width = width
        else:
            width = fontsize / 24.0

        if cols:
            cols = cols
        else:
            cols = 1

        vectors = vectorInstr['list']
        labels = [vector[4] for vector in vectors if vector[3] != 0]
        extent = (len(max(labels, key=len)) * fontsize / 2, fontsize)
        wExtent = self.unitConv.convert(
            value=extent[0], fromUnit='point', toUnit='inch')
        hExtent = self.unitConv.convert(
            value=extent[1], fromUnit='point', toUnit='inch')
        w = (width + wExtent) * cols
        h = len(labels) * hExtent / cols
        h *= 1.1
        return (w, h)


class Raster(InstructionObject):
    """Class representing raster instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'raster'
        # default values
        self.defaultInstruction = dict(isRaster=False, raster=None)
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        instr = string.Template("raster $raster").substitute(self.instruction)
        return instr

    def Read(self, instruction, text):
        """Read instruction and save information"""
        instr = {}
        instr['isRaster'] = True
        try:
            map = text.split()[1]
        except IndexError:
            GError(_("Failed to read instruction %s") % instruction)
            return False
        try:
            info = grass.find_file(map, element='cell')
        except grass.ScriptError as e:
            GError(message=e.value)
            return False
        instr['raster'] = info['fullname']

        self.instruction.update(instr)
        return True


class Vector(InstructionObject):
    """Class keeps vector layers"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'vector'
        # default values
        self.defaultInstruction = dict(
            list=None)  # [vmap, type, id, lpos, label]
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        return ''

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        instr = {}

        for line in text:
            if line.startswith('vpoints') or line.startswith(
                    'vlines') or line.startswith('vareas'):
                # subtype
                if line.startswith('vpoints'):
                    subType = 'points'
                elif line.startswith('vlines'):
                    subType = 'lines'
                elif line.startswith('vareas'):
                    subType = 'areas'
                # name of vector map
                vmap = line.split()[1]
                try:
                    info = grass.find_file(vmap, element='vector')
                except grass.ScriptError as e:
                    GError(message=e.value)
                    return False
                vmap = info['fullname']
                # id
                id = kwargs['id']
                # lpos
                lpos = kwargs['vectorMapNumber']
                # label
                label = '('.join(vmap.split('@')) + ')'
                break
        instr = [vmap, subType, id, lpos, label]
        if not self.instruction['list']:
            self.instruction['list'] = []
        self.instruction['list'].append(instr)

        return True


class VProperties(InstructionObject):
    """Class represents instructions vareas, vlines, vpoints"""

    def __init__(self, id, subType, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'vProperties'
        self.subType = subType
        # default values
        if self.subType == 'points':
            dd = dict(
                subType='points',
                name=None,
                type='point or centroid',
                connection=False,
                layer='1',
                masked='n',
                color='0:0:0',
                width=1,
                fcolor='255:0:0',
                rgbcolumn=None,
                symbol=os.path.join(
                    'basic',
                    'x'),
                eps=None,
                size=5,
                sizecolumn=None,
                scale=None,
                rotation=False,
                rotate=0,
                rotatecolumn=None,
                label=None,
                lpos=None)
        elif self.subType == 'lines':
            dd = dict(
                subType='lines',
                name=None,
                type='line or boundary',
                connection=False,
                layer='1',
                masked='n',
                color='0:0:0',
                hwidth=1,
                hcolor='none',
                rgbcolumn=None,
                width=1,
                cwidth=None,
                style='solid',
                linecap='butt',
                label=None,
                lpos=None)
        else:  # areas
            dd = dict(subType='areas', name=None, connection=False, layer='1',
                      masked='n', color='0:0:0', width=1,
                      fcolor='none', rgbcolumn=None,
                      pat=None, pwidth=1, scale=1, label=None, lpos=None)
        self.defaultInstruction = dd
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        dic = self.instruction
        vInstruction = string.Template("v$subType $name\n").substitute(dic)
        # data selection
        if self.subType in ('points', 'lines'):
            vInstruction += string.Template("    type $type\n").substitute(dic)
        if dic['connection']:
            vInstruction += string.Template(
                "    layer $layer\n").substitute(dic)
            if 'cats' in dic:
                vInstruction += string.Template(
                    "    cats $cats\n").substitute(dic)
            elif 'where' in dic:
                vInstruction += string.Template(
                    "    where $where\n").substitute(dic)
        vInstruction += string.Template("    masked $masked\n").substitute(dic)
        # colors
        vInstruction += string.Template("    color $color\n").substitute(dic)
        if self.subType in ('points', 'areas'):
            if dic['color'] != 'none':
                vInstruction += string.Template(
                    "    width $width\n").substitute(dic)
            if dic['rgbcolumn']:
                vInstruction += string.Template(
                    "    rgbcolumn $rgbcolumn\n").substitute(dic)
            vInstruction += string.Template(
                "    fcolor $fcolor\n").substitute(dic)
        else:
            if dic['rgbcolumn']:
                vInstruction += string.Template(
                    "    rgbcolumn $rgbcolumn\n").substitute(dic)
            elif dic['hcolor'] != 'none':
                vInstruction += string.Template(
                    "    hwidth $hwidth\n").substitute(dic)
                vInstruction += string.Template(
                    "    hcolor $hcolor\n").substitute(dic)

        # size and style
        if self.subType == 'points':
            if not dic['eps']:
                vInstruction += string.Template(
                    "    symbol $symbol\n").substitute(dic)
            else:  # eps
                vInstruction += string.Template(
                    "    eps $eps\n").substitute(dic)
            if dic['size']:
                vInstruction += string.Template(
                    "    size $size\n").substitute(dic)
            else:  # sizecolumn
                vInstruction += string.Template(
                    "    sizecolumn $sizecolumn\n").substitute(dic)
                vInstruction += string.Template(
                    "    scale $scale\n").substitute(dic)
            if dic['rotation']:
                if dic['rotate'] is not None:
                    vInstruction += string.Template(
                        "    rotate $rotate\n").substitute(dic)
                else:
                    vInstruction += string.Template(
                        "    rotatecolumn $rotatecolumn\n").substitute(dic)

        if self.subType == 'areas':
            if dic['pat'] is not None:
                patternFile = dic['pat'].replace(
                    os.getenv("GISBASE"), "$GISBASE")
                vInstruction += "    pat %s\n" % patternFile
                vInstruction += string.Template(
                    "    pwidth $pwidth\n").substitute(dic)
                vInstruction += string.Template(
                    "    scale $scale\n").substitute(dic)

        if self.subType == 'lines':
            if dic['width'] is not None:
                vInstruction += string.Template(
                    "    width $width\n").substitute(dic)
            else:
                vInstruction += string.Template(
                    "    cwidth $cwidth\n").substitute(dic)
            vInstruction += string.Template(
                "    style $style\n").substitute(dic)
            vInstruction += string.Template(
                "    linecap $linecap\n").substitute(dic)
        #position and label in vlegend
        vInstruction += string.Template(
            "    label $label\n    lpos $lpos\n").substitute(dic)

        vInstruction += "    end"
        return vInstruction

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        instr = {}
        try:
            info = grass.find_file(name=text[0].split()[1], element='vector')
        except grass.ScriptError as e:
            GError(message=e.value)
            return False
        instr['name'] = info['fullname']
        # connection
        instr['connection'] = True
        self.mapDBInfo = VectorDBInfo(instr['name'])
        self.layers = self.mapDBInfo.layers.keys()
        if not self.layers:
            instr['connection'] = False

        # points
        if text[0].startswith('vpoints'):
            for line in text[1:]:
                if line.startswith('type'):
                    tp = []
                    if line.find('point') != -1:
                        tp.append('point')
                    if line.find('centroid') != -1:
                        tp.append('centroid')
                    instr['type'] = ' or '.join(tp)
                elif line.startswith('fcolor'):
                    instr['fcolor'] = line.split()[1]
                elif line.startswith('rgbcolumn'):
                    instr['rgbcolumn'] = line.split()[1]
                elif line.startswith('symbol'):
                    instr['symbol'] = line.split()[1]
                elif line.startswith('eps'):
                    instr['eps'] = line.split()[1]
                elif line.startswith('size '):
                    instr['size'] = line.split()[1]
                elif line.startswith('sizecolumn'):
                    instr['size'] = None
                    instr['sizecolumn'] = line.split()[1]
                elif line.startswith('scale '):
                    instr['scale'] = float(line.split()[1])
                elif line.startswith('rotate '):
                    instr['rotation'] = True
                    instr['rotate'] = line.split()[1]
                elif line.startswith('rotatecolumn'):
                    instr['rotatecolumn'] = line.split()[1]
                    instr['rotation'] = True
                    instr['rotate'] = None

        # lines
        elif text[0].startswith('vlines'):
            for line in text[1:]:
                if line.startswith('type'):
                    tp = []
                    if line.find('line') != -1:
                        tp.append('line')
                    if line.find('boundary') != -1:
                        tp.append('boundary')
                    instr['type'] = ' or '.join(tp)
                elif line.startswith('hwidth'):
                    instr['hwidth'] = float(line.split()[1])
                elif line.startswith('hcolor'):
                    instr['hcolor'] = line.split()[1]
                elif line.startswith('rgbcolumn'):
                    instr['rgbcolumn'] = line.split()[1]
                elif line.startswith('cwidth'):
                    instr['cwidth'] = float(line.split()[1])
                    instr['width'] = None
                elif line.startswith('style'):
                    instr['style'] = line.split()[1]
                elif line.startswith('linecap'):
                    instr['linecap'] = line.split()[1]

        elif text[0].startswith('vareas'):
            for line in text[1:]:
                if line.startswith('fcolor'):
                    instr['fcolor'] = line.split()[1]
                elif line.startswith('pat'):
                    patternFile = line.split()[1]
                    instr['pat'] = patternFile.replace(
                        "$GISBASE", os.getenv("GISBASE"))
                elif line.startswith('pwidth'):
                    instr['pwidth'] = float(line.split()[1])
                elif line.startswith('scale'):
                    instr['scale'] = float(line.split()[1])

        # same properties for all
        for line in text[1:]:
            if line.startswith('lpos'):
                instr['lpos'] = int(line.split()[1])
            elif line.startswith('label'):
                instr['label'] = line.split(None, 1)[1]
            elif line.startswith('layer'):
                instr['layer'] = line.split()[1]
            elif line.startswith('masked'):
                if line.split()[1].lower() in ('y', 'yes'):
                    instr['masked'] = 'y'
                else:
                    instr['masked'] = 'n'
            elif line.startswith('color'):
                instr['color'] = line.split()[1]
            elif line.startswith('rgbcolumn'):
                instr['rgbcolumn'] = line.split()[1]
            elif line.startswith('width'):
                instr['width'] = float(line.split()[1])

        if 'label' not in instr:
            instr['label'] = '('.join(instr['name'].split('@')) + ')'
        if 'lpos' not in instr:
            instr['lpos'] = kwargs['vectorMapNumber']
        self.instruction.update(instr)

        return True


class Labels(InstructionObject):
    """Class representing labels instruction"""

    def __init__(self, id, env):
        InstructionObject.__init__(self, id=id, env=env)
        self.type = 'labels'
        # default values
        self.defaultInstruction = dict(labels=[])
        # current values
        self.instruction = dict(self.defaultInstruction)

    def __str__(self):
        instr = ''
        for label in self.instruction['labels']:
            instr += "labels %s\n" % label
            instr += "end\n"
        return instr

    def Read(self, instruction, text, **kwargs):
        """Read instruction and save information"""
        for line in text:
            try:
                if line.startswith('labels'):
                    labels = line.split(None, 1)[1]
                    self.instruction['labels'].append(labels)
            except(IndexError, ValueError):
                GError(_("Failed to read instruction %s") % instruction)
                return False

        return True
