
########################################################################################################################
# Copyright (c) 2015, University of Osnabrueck                                                                         #
#   All rights reserved.                                                                                               #
#                                                                                                                      #
#   Redistribution and use in source and binary forms, with or without modification, are permitted provided that the   #
#   following conditions are met:                                                                                      #
#                                                                                                                      #
#   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following#
#       disclaimer.                                                                                                    #
#                                                                                                                      #
#   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       #
#       following disclaimer in the documentation and/or other materials provided with the distribution.               #
#                                                                                                                      #
#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, #
#   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  #
#   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, #
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR    #
#   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  #
#   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE   #
#   USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                           #
########################################################################################################################
import numpy as np
import StringIO
import zipfile

POINT = 0
LINE = 1
AREA = 2
CUBIC = 3
LIST = 4

DEBUG = 0

def parseResFile(filename, requestedTransmitter, requestedLayer = None):
    if type(filename) is zipfile.ZipExtFile:
        f = filename
    else:
        f = open(filename)

    transmitter = None
    res = []
    #res.append([])

    input = f.read().split('\n')

    #remove lastline
    input = input[0:-1]
    f.close()



    #First line contains transmitter metainformation
    headtr = np.genfromtxt(StringIO.StringIO(input[0]))

    transmitters = parseTransmitterHeader(headtr)
    print "Available transmitters:\n", transmitters

    #remove first line
    input = input[1:]



    #second line contrains receiver metainformation
    headrec = np.genfromtxt(StringIO.StringIO(input[0]))
    t = int(headrec[0])

    #remove second line
    input = input[1:]

    borders, stp, height, leng = parseHead(headrec, t)
    if DEBUG: print "bd, steps, height, len:", borders, stp, height, leng

    if DEBUG: print "transmitters:", len(transmitters)

    if t in [CUBIC, AREA]:

        lz = leng[2]
        ly = leng[1]
        ll = 0

        for tr in range(0, len(transmitters), 1):
            res.append([])
            for z in range(0, lz, 1):
                area = input[ll + z*ly : ll + (z+1)*ly]
                result = parseArea(area)
                if DEBUG: print len(result)
                res[tr].append(result)
            ll += (z+1)*ly
            if DEBUG: print tr

    if t == LINE:
        for t in range(0, len(transmitters), 1):
            res.append([])
            area = input[t : (t+1)]
            result = parseArea(area)
            res[t].append(result)

    if DEBUG: print res
    if DEBUG: print "transmitters:", len(res)

    #Ugly hack, deteremine layer
    if requestedLayer == None:
        #res = res[0]
        layerid = 0
    else:
        if DEBUG: print "layers: ", len(res[0]), " | lowLayer: ", height[0], " | reqL: ",requestedLayer, " | index: ", max(int(requestedLayer/stp[2])-1,0)

        layer = max(int(requestedLayer/stp[2])-1, 0)
        if layer >= len(res[0]):
            print "Chosen layer is to high, displaying highest..."
            layer = len(res[0]) -1
        layerid = layer
        #res = res[layer]


    #find transmitter
    #not very fast, but functional
    transmitter = transmitters[0]
    trid = -1
    i = 0
    for tr in transmitters:
        tr = np.array(tr)
        #print "reqTr", "no" if requestedTransmitter is None else requestedTransmitter, tr, np.linalg.norm(requestedTransmitter - tr)
        if requestedTransmitter is None or np.linalg.norm(requestedTransmitter - tr) < 0.5:
            if DEBUG and not requestedTransmitter == None:
                print "found"
            transmitter = tr
            trid = i
            break
        i += 1

    if trid == -1:
        print "Did not found: ", requestedTransmitter, " Choose another transmitter\nUsing ", transmitter
        trid = 0
    if DEBUG: print "Using transmitter:", transmitter



    lt = len(res)
    ll = len(res[0])
    ly = len(res[0][0])
    lx = len(res[0][0][0])

    if DEBUG:
        print "Transmitter: ", lt
        print "Layer:       ", ll
        print "x y:         ", lx, ly

    res = res[trid]
    res = res[layerid]

    #print "RES\n",res

    for y in range(0, ly):
        for x in range(0, lx):
            if res[y][x] < -1000:
                res[y][x] = np.inf

    #print res

    return np.array(res), borders, transmitter, stp

def parseHead(head, type):
    stp = [0,0,1]
    leng = [0,0,1]
    height = [0,0]
    borders = [0,0,0,0]

    if type == AREA:
        height[0] = height[1] = head[5]
        stp[0] = head[6]
        stp[1] = head[7]
        borders = head[1:5]
        leng[0] = int((borders[2] - borders[0]) / stp[0]) + 1
        leng[1] = int((borders[3] - borders[1]) / stp[1]) + 1

    if type == CUBIC:
        stp = head[7:]
        borders[:2] = head[1:3]
        borders[2:] = head[4:6]
        height[0] = head[3]
        height[1] = head[6]
        leng[0] = int((borders[2] - borders[0]) / stp[0]) + 1
        leng[1] = int((borders[3] - borders[1]) / stp[1]) + 1
        leng[2] = int(( height[1] -  height[0]) / stp[2]) + 1

    if type == POINT:
        borders[:2] = head[1:3]
        height[0] = head[3]

    if type == LINE:
        borders[:2] = head[1:3]
        borders[2:] = head[4:6]
        height[0] = head[3]
        height[1] = head[6]
        stp[2] = head[7]

    return borders, stp, height, leng


def parseTransmitterHeader(head):

    transmitters = []
    transmitterType = int(head[0])

    if transmitterType == POINT:
        transmitters.append([head[1], head[2], head[3]])

    elif transmitterType == LIST:

        headtr = head[2:]
        while True:
            #print len(headtr)
            if len(headtr) == 0:
                break
            #print len(headtr)
            transmitter = [headtr[0], headtr[1], headtr[2]]
            headtr = headtr[3:]
            transmitters.append(transmitter)

    #not very fast, but functional
    elif transmitterType == AREA:
        borders, stp, height, leng = parseHead(head, transmitterType)
        for y in frange(borders[1], borders[3], stp[1]):
            for x in frange(borders[0], borders[2], stp[0]):
                transmitters.append([x, y, height[0]])

    elif transmitterType == CUBIC:
        borders, stp, height, leng = parseHead(head, transmitterType)
        for z in frange(height[0], height[1], stp[2]):
            for y in frange(borders[1], borders[3], stp[1]):
                for x in frange(borders[0], borders[2], stp[0]):
                    transmitters.append([x, y, z])

    return transmitters


def frange(x, y, jump):
    while x < y:
        yield x
        x += jump


def parseArea(input):
    result = []

    for l in input:
        if len(l.strip()) > 0:
            tmp = np.genfromtxt(StringIO.StringIO(l), delimiter=" ").tolist()
            result.append(tmp)

    return result


def parsePoint(input):
    result = []

    for l in input:
        signal = float(l)
        result.append(signal)

    return result


def parseList(input):
    return np.genfromtext(StringIO.StringIO(input), delimiter=" ").tolist()

