#!/usr/bin/env python
# Copyright (c) 2007-8 Qtrac Ltd. All rights reserved.
# This program or module is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 2 of the License, or
# version 3 of the License, or (at your option) any later version. It is
# provided for educational purposes and is distributed in the hope that
# it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
# the GNU General Public License for more details.

import platform
import csv

from PyQt4.QtCore import *
from PyQt4.QtGui import *

PCI, PCJ, LOCX, LOCY, LOCZ, CHECK, ERROR, PIXERROR = range(8)

MAGIC_NUMBER = 0x570C4
FILE_VERSION = 1


class GCP(object):
    def __init__(self, picture_i=0., picture_j=0., local_x=0., local_y=0., local_z=0., check = 1, error = 0, pixerror = 0):

        self.picture_i = picture_i
        self.picture_j = picture_j
        self.local_x = local_x
        self.local_y = local_y
        self.local_z = local_z
        self.check = check
        self.error = error
        self.pixerror = pixerror


class GCPTableModel(QAbstractTableModel):
    def __init__(self, filename=""):
        super(GCPTableModel, self).__init__()
        self.filename = filename
        self.dirty = False
        self.GCPs = []
        self.pictures_i = set()
        self.pictures_j = set()
        self.locals_x = set()
        self.locals_y = set()
        self.locals_z = set()
        self.checks = set()
        self.errors = set()
        self.pixerrors = set()
        
    def flags(self, index):
        if not index.isValid():
            return Qt.ItemIsEnabled
        return Qt.ItemFlags(QAbstractTableModel.flags(self, index)|
                            Qt.ItemIsEditable)


    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid() or \
           not (0 <= index.row() < len(self.GCPs)):
            return 
        GCP = self.GCPs[index.row()]
        column = index.column()
        if role == Qt.DisplayRole:
            if column == PCI:
                return  GCP.picture_i
            elif column == PCJ:
                return  GCP.picture_j
            elif column == LOCX:
                return  GCP.local_x
            elif column == LOCY:
                return  GCP.local_y
            elif column == LOCZ:
                return  GCP.local_z
            elif column == CHECK:
                return  GCP.check
            elif column == ERROR:
                return  GCP.error
            elif column == PIXERROR:
                return  GCP.pixerror
        elif role == Qt.TextAlignmentRole:
            return  int(Qt.AlignLeft|Qt.AlignVCenter)
        elif role == Qt.BackgroundColorRole:
                return  QColor(210, 230, 230)
        return


    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role == Qt.TextAlignmentRole:
            if orientation == Qt.Horizontal:
                return  int(Qt.AlignLeft|Qt.AlignVCenter)
            return  int(Qt.AlignRight|Qt.AlignVCenter)
        if role != Qt.DisplayRole:
            return  
        if orientation == Qt.Horizontal:
            if section == PCI:
                return  "Picture u"
            elif section == PCJ:
                return  "Picture v"
            elif section == LOCX:
                return  "World x"
            elif section == LOCY:
                return  "World y"
            elif section == LOCZ:
                return  "World z"
            elif section == CHECK:
                return  "Use as GCP"
            elif section == ERROR:
                return  "3D error [m]"
            elif section == PIXERROR:
                return  "Pixel error"
        return  int(section + 1)


    def rowCount(self, index=QModelIndex()):
        return len(self.GCPs)


    def columnCount(self, index=QModelIndex()):
        return 8


    def setData(self, index, value, role=Qt.EditRole):
        if index.isValid() and 0 <= index.row() < len(self.GCPs):
            GCP = self.GCPs[index.row()]
            column = index.column()
            if type(value) == unicode:
                try:
                    value = float(value)
                except:
                    value = 0
            if column == PCI:
                GCP.picture_i = value
            elif column == PCJ:
                GCP.picture_j = value
            elif column == LOCX:
                GCP.local_x = value
            elif column == LOCY:
                GCP.local_y = value
            elif column == LOCZ:
                GCP.local_z = value
            elif column == CHECK:
                GCP.check = value
            elif column == ERROR:
                GCP.error = value
            elif column == PIXERROR:
                GCP.pixerror = value
            self.dirty = True
            self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"),
                      index, index)
            return True
        return False


    def insertRows(self, position, rows=1, index=QModelIndex()):
        self.beginInsertRows(QModelIndex(), position,position + rows - 1)
        for row in range(rows):
            self.GCPs.insert(position + row,GCP())
        self.endInsertRows()
        self.dirty = True
        return True

    def removeRows(self, position, rows=1, index=QModelIndex()):
        self.beginRemoveRows(QModelIndex(), position,
                             position + rows - 1)
        self.GCPs = self.GCPs[:position] + \
                     self.GCPs[position + rows:]
        self.endRemoveRows()
        self.dirty = True
        return True


    def load(self, filename):

        exception = None
        fh = None
        
        self.filename = filename####
        
        try:
            if not filename:
                raise IOError, "no filename specified for loading"
            
            elif filename.find('.dat') != -1:
                fh = QFile(filename)
                if not fh.open(QIODevice.ReadOnly):
                    raise IOError, unicode(fh.errorString())
                stream = QDataStream(fh)
                magic = stream.readInt32()
                if magic != MAGIC_NUMBER:
                    raise IOError, "unrecognized file type"
                fileVersion = stream.readInt16()
                if fileVersion != FILE_VERSION:
                    raise IOError, "unrecognized file type version"
                self.GCPs = []
                while not stream.atEnd():
                    picture_i = stream.readQVariant()
                    picture_j = stream.readQVariant()
                    local_x = stream.readQVariant()
                    local_y = stream.readQVariant()
                    local_z  = stream.readQVariant()
                    self.GCPs.append(GCP(picture_i,picture_j,local_x,local_y,local_z))
                    self.pictures_i.add(picture_i)
                    self.pictures_j.add(picture_j)
                    self.locals_x.add(local_x)
                    self.locals_y.add(local_y)
                    self.locals_z.add(local_z)
                self.dirty = False
                
            elif filename.find('.csv') != -1:
                f = open(filename, 'rb')
                try:
                    gcpReader = csv.reader(f)
                    
                    i= 0
                    for row in gcpReader:
                        if i!=0:
                            picture_i = float(row[0])
                            picture_j = float(row[1])
                            local_x = float(row[2])
                            local_y = float(row[3])
                            local_z  = float(row[4])
                            
                            self.GCPs.append(GCP(picture_i,picture_j,local_x,local_y,local_z))
                            self.pictures_i.add(picture_i)
                            self.pictures_j.add(picture_j)
                            self.locals_x.add(local_x)
                            self.locals_y.add(local_y)
                            self.locals_z.add(local_z)
                        i+=1

                finally:
                    f.close()
                
        except IOError, e:
            exception = e
        finally:
            if fh is not None:
                fh.close()
            if exception is not None:
                raise exception

    def save(self, filename):
        
        self.filename = filename##########
        exception = None
        fh = None
        try:
            if not filename:
                raise IOError, "no filename specified for saving"
            if filename.find('.dat')==-1:
                filename = filename+'.dat'
            
            fh = QFile(filename)
            if not fh.open(QIODevice.WriteOnly):
                raise IOError, unicode(fh.errorString())
            stream = QDataStream(fh)
            stream.writeInt32(MAGIC_NUMBER)
            stream.writeInt16(FILE_VERSION)
            if hasattr(QDataStream,'Qt_4_8'):
                stream.setVersion(QDataStream.Qt_4_8)
            for GCP in self.GCPs:
                stream.writeQVariant(GCP.picture_i)
                stream.writeQVariant(GCP.picture_j)
                stream.writeQVariant(GCP.local_x)
                stream.writeQVariant(GCP.local_y)
                stream.writeQVariant(GCP.local_z)
            self.dirty = False
        except IOError, e:
            exception = e
        finally:
            if fh is not None:
                fh.close()
            if exception is not None:
                raise exception
                
        #Save as CSV
        ############
        filename = filename.replace('.dat','.csv')
        if filename.find('.csv')==-1:
            filename = filename+'.csv'
            
        f = open(filename, 'wb')
        try:
            writer = csv.writer(f)
            writer.writerow( ('line', 'column', 'X', 'Y', 'Z') )
            for GCP in self.GCPs:
                writer.writerow( (GCP.picture_i, GCP.picture_j, GCP.local_x, GCP.local_y, GCP.local_z) )
        finally:
            f.close()
        

    def checkValid(self,rowIndex):
        valid = 1
        for i in range(0,8):
            index = self.index(rowIndex, i)
            dat = self.data(index)
            if not isinstance(dat, (int, long, float)):
                valid = 0
        return valid

