__title__ = 'hsgconv'
__version__ = '1.1.0'

#import statements
import sys
import os


#python 3 compatibility
PYTHON3 = sys.version_info[0] == 3

if PYTHON3:
    xrange = range
    izip = zip
else:
    from itertools import izip


#Begin Module

def GridParams(gridID, mean_z):
    """
    Retrieve conversion params specific to a grid

    :param gridID: string, refer to the list at http://www.standardsforhighways.co.uk/ha/standards/ians/pdfs/ian99.pdf
    :param mean_z: float, mean altitude of the scheme's bounding box

    :returns: Conversion_parameters: tuple, `('gridID, PSF, ESF, CSF, 1/CSF, Eo, No')`
    """
    ESF = __CalculateESF(mean_z)
    (Es,Ns,Eo,No,PSF,bands) = __LocalParams(gridID)
    return (gridID,PSF,ESF,(PSF*ESF), 1/(PSF*ESF) ,Eo,No)


def CreateParamList(gridID, PSF, ESF, Eo, No):
    """
    Create a custom parameters list. **Be careful** of your inputs

    :param gridID: string, refer to the list at http://www.standardsforhighways.co.uk/ha/standards/ians/pdfs/ian99.pdf
    :param PSF: float - Projection Scale Factor
    :param ESF: float - Elevation Scale Factor
    :param Eo: float - delta Easting
    :param No: float - delta Northing
    
    :returns: Custom parameters for coordinate conversion
    """
    CSF = PSF*ESF
    return (gridID, PSF, ESF, CSF, 1/CSF, float(Eo), float(No))


def ConvertToOSBG(myGridParam, x_coord, y_coord):
    """
    Convert local grid's (x,y) to OBSG's (easting, northing)
    
    :param myGridParam: conversion parameter of local grid `('gridID, PSF, ESF, CSF, 1/CSF, Eo, No')`
    :param x_coord: x coordinate in local grid
    :param y_coord: y coordinate in local grid
    
    :returns: OSBG36's Easting and Northing
    """
    OSBG_coord = __ConvertLocalToOBSG(x_coord, y_coord, myGridParam[5], myGridParam[6], myGridParam[3])
    return OSBG_coord


def ConvertToLocalGrid(myGridParam, easting, northing):
    """
    Convert OSBG's (easting, northing) to local grid's (x,y)
    
    :param myGridParam: conversion parameter of local grid as ('gridID, PSF, ESF, CSF, 1/CSF, Eo, No')
    :param easting: easting in OSBG36
    :param northing: northing in OSBG36
    
    :returns: HS local grid's X and Y coordinates
    """
    local_coord = __ConvertOSBGToLocal(easting, northing, myGridParam[5], myGridParam[6], myGridParam[4])
    return local_coord


#non-exposed functions

def __GetBandsData():
    """
    Get Local Grid's elevation parameter

    :param _: todo
    
    :returns: todo
    """
    this_dir, this_filename = os.path.split(__file__)
    DATA_PATH = os.path.join(this_dir, "data", "LocalGrid_DataBands.csv")
    return open(DATA_PATH)


def __GetGridData():
    """
    Get Local Grid's transformation parameters

    :param _: todo
    
    :returns: todo
    """
    this_dir, this_filename = os.path.split(__file__)
    DATA_PATH = os.path.join(this_dir, "data", "LocalGrid_DataParams.csv")
    return open(DATA_PATH)


def  __ConvertLocalToOBSG(e_local, n_local, Eo, No, CSF):
    """
    Convert local grid to OSBG coordinates

    :param e_local: x coord in local grid
    :param n_local: y coord in local grid
    :param Eo: delta easting of local grid
    :param No: delta northing of local grid
    :param CSF: grid's associated scale factor
    
    :returns: todo
    """
    #Easting national grid
    easting = e_local*CSF + Eo
    #Northing national grid
    northing = n_local*CSF + No
    #Return a tuple of coordinates
    return (easting, northing)


def  __ConvertOSBGToLocal(easting, northing, Eo, No, one_over_CSF):
    """
    Convert OSBG36 Easting-Northing to local grid coordinates

    :param easting: easting in OSBG36
    :param northing: northing in OSBG36
    :param Eo: delta easting of local grid
    :param No: delta northing of local grid
    :param one_over_CSF: reciprocal CSF (combinated scale factor, = 1/CSF)

    :returns: todo
    """

    #x-coord in local grid
    x_local = (easting - Eo)*(one_over_CSF)
    #y-coord in local grid
    y_local = (northing - No)*(one_over_CSF)
    #return a tuple of coordinates    
    return (x_local, y_local)


def __CalculateESF(myElevation):
    """
    Retrieve the ElevationScaleFactor

    :param _: todo
    
    :returns: todo
    """
    #list out elevation and elevation scale factors
    elevation_list = [0,80,160,240,320,400,480,520,640,720,800]
    ESF_list = [1,0.9999875,0.999975,0.9999625,0.99995,0.9999375,0.999925,0.9999125,0.9999,0.9998875,0.999875]
    
    #find at which elevation band myElevation is
    closestAltitude = min(elevation_list, key=lambda x:abs(x-myElevation))
    #find the index of the elevation
    index = elevation_list.index(closestAltitude)
    ESF = ESF_list[index]

    return float(ESF)


def __LocalParams(localGrid):
    """
    Retrieve the parameters from the shared csv with files inside
    
    :param _: todo
    
    :returns: todo
    """
    #check valuable input
    if  (localGrid == None):
        return None
    
    try:
        f = __GetGridData()
    except:
        print ("I have not found any references for this grid")
        return None
    
    #parameters
    foundRecord = False
    Es = None
    Ns = None
    Eo = None
    No = None
    SF = None
    bands = None
    
    #scan lines
    for line in f:
        currentLine = line.split(',')
        if (currentLine[0] == localGrid):
            foundRecord = True
            print("I have found a record for grid zone " + currentLine[0])
            Es = float(currentLine[1])
            Ns = float(currentLine[2])
            Eo = float(currentLine[3])
            No = float(currentLine[4])
            SF = float(currentLine[5])
            bands = currentLine[6].split('*')
            break
            
    if foundRecord:
        #return a list with values
        return (Es,Ns,Eo,No,SF,bands)
    else:
        print("I haven't found any records for the requested grid zone")
        return None   
