from ctypes import *
import numpy as np

def checkInputList(input_list, n):
    if not isinstance(input_list, list):
        raise TypeError("Input type is not list!", input_list)
    for i in input_list:
        if type(i) != type(1.1):
            if type(i) != type(1):
                raise TypeError("Input list's element is not float or int!", input_list)
    if len(input_list) != n:
        raise RuntimeError("Length of input list is not equal to stars' number!", input_list)
            
def on_orbit_obs_position(input_ra_list, input_dec_list, input_pmra_list, input_pmdec_list, input_rv_list, input_parallax_list, input_nstars, input_x, input_y, input_z, input_vx, input_vy, input_vz, input_epoch, input_date_str, input_time_str, lib_path):
    #Check input parameters
    if not isinstance(input_nstars, int):
        raise TypeError("Parameter 7 is not int!", input_nstars)
    
    checkInputList(input_ra_list, input_nstars)
    checkInputList(input_dec_list, input_nstars)
    checkInputList(input_pmra_list, input_nstars)
    checkInputList(input_pmdec_list, input_nstars)
    checkInputList(input_rv_list, input_nstars)
    checkInputList(input_parallax_list, input_nstars)
    
    if not isinstance(input_x, float):
        raise TypeError("Parameter 8 is not double!", input_x)
    if not isinstance(input_y, float):
        raise TypeError("Parameter 9 is not double!", input_y)
    if not isinstance(input_z, float):
        raise TypeError("Parameter 10 is not double!", input_z)
    if not isinstance(input_vx, float):
        raise TypeError("Parameter 11 is not double!", input_vx)
    if not isinstance(input_vy, float):
        raise TypeError("Parameter 12 is not double!", input_vy)
    if not isinstance(input_vz, float):
        raise TypeError("Parameter 13 is not double!", input_vz)
    #Convert km -> m
    input_x = input_x*1000.0 
    input_y = input_y*1000.0 
    input_z = input_z*1000.0
    input_vx = input_vx*1000.0
    input_vy = input_vy*1000.0
    input_vz  = input_vz*1000.0
    
    if not isinstance(input_date_str, str):
        raise TypeError("Parameter 15 is not string!", input_date_str)
    else:
        input_date_str = input_date_str.strip()
        if not (input_date_str[4]=="-" and input_date_str[7]=="-"):
            raise TypeError("Parameter 15 format error (1)!", input_date_str)
        else:
            tmp = input_date_str.split("-")
            if len(tmp) != 3:
                raise TypeError("Parameter 15 format error (2)!", input_date_str)
            input_year = int(tmp[0])
            input_month = int(tmp[1])
            input_day = int(tmp[2])
            if not (input_year>=1900 and input_year<=2100):
                raise TypeError("Parameter 15 year range error [1900 ~ 2100]!", input_year)
            if not (input_month>=1 and input_month<=12):
                raise TypeError("Parameter 15 month range error [1 ~ 12]!", input_month)
            if not (input_day>=1 and input_day<=31):
                raise TypeError("Parameter 15 day range error [1 ~ 31]!", input_day)
    
    if not isinstance(input_time_str, str):
        raise TypeError("Parameter 16 is not string!", input_time_str)
    else:
        input_time_str = input_time_str.strip()
        if not (input_time_str[2]==":" and input_time_str[5]==":"):
            raise TypeError("Parameter 16 format error (1)!", input_time_str)
        else:
            tmp = input_time_str.split(":")
            if len(tmp) != 3:
                raise TypeError("Parameter 16 format error (2)!", input_time_str)
            input_hour = int(tmp[0])
            input_minute = int(tmp[1])
            input_second = float(tmp[2])
            if not (input_hour>=0 and input_hour<=23):
                raise TypeError("Parameter 16 hour range error [0 ~ 23]!", input_hour)
            if not (input_minute>=0 and input_minute<=59):
                raise TypeError("Parameter 16 minute range error [0 ~ 59]!", input_minute)
            if not (input_second>=0 and input_second<60.0):
                raise TypeError("Parameter 16 second range error [0 ~ 60)!", input_second)
    #Inital dynamic lib
    shao = cdll.LoadLibrary(lib_path)
    shao.onOrbitObs.restype = c_int
    
    d3 = c_double * 3
    shao.onOrbitObs.argtypes = [c_double, c_double, c_double, c_double, c_double, c_double, \
                                c_int, c_int, c_int, c_int, c_int, c_double, \
                                c_double, POINTER(d3), POINTER(d3), \
                                c_int, c_int, c_int, c_int, c_int, c_double, \
                                POINTER(c_double), POINTER(c_double) ]
    output_ra_list = list()
    output_dec_list = list()
    for i in range(input_nstars):
        input_ra = c_double(input_ra_list[i])
        input_dec = c_double(input_dec_list[i])
        input_pmra = c_double(input_pmra_list[i])
        input_pmdec = c_double(input_pmdec_list[i])
        input_rv = c_double(input_rv_list[i])
        input_parallax = c_double(input_parallax_list[i])
        p3 = d3(input_x, input_y, input_z)
        v3 = d3(input_vx, input_vy, input_vz)
        input_year_c = c_int(input_year)
        input_month_c = c_int(input_month)
        input_day_c = c_int(input_day) 
        input_hour_c = c_int(input_hour)
        input_minute_c = c_int(input_minute)
        input_second_c  = c_double(input_second)
        DAT = c_double(37.0)
        output_ra = c_double(0.0)
        output_dec = c_double(0.0)
        rs = shao.onOrbitObs(input_ra, input_dec, input_pmra, input_pmdec, input_rv, input_parallax, \
                             input_year_c, input_month_c, input_day_c, input_hour_c, input_minute_c, input_second_c, \
                             DAT, byref(p3), byref(v3), \
                             input_year_c, input_month_c, input_day_c, input_hour_c, input_minute_c, input_second_c, \
                             byref(output_ra), byref(output_dec))
        if rs != 0:
            raise RuntimeError("Calculate error!")
        output_ra_list.append(output_ra.value)
        output_dec_list.append(output_dec.value)
    return np.array(output_ra_list), np.array(output_dec_list)
