#############################################################################################################
##########################the program is imported where all the requisite lists are already constructed#####
#############################################################################################################
import pandas as pd
import preprocess
import numpy as np

#############################################################################################################
##Variables for the optimization program are written into the model file here################################
#############################################################################################################

def variables():
  mod.write('#Decision variables\n\n')
  mod.write('var arr {100..'+str(num_arr)+'} >=0, <=179.5;\n')
  mod.write('var dep {1000..'+str(num_dep)+'} >=0, <=179.5;\n')
  mod.write('var pd { i in {1000..'+str(num_dep)+'},j in {1000..'+str(num_dep)+'} : i<>j} integer <=1;\n')
  mod.write('var pad { i in {100..'+str(num_arr)+'},j in {100..'+str(num_dep)+'} : i<>j} integer <=1;\n')
  mod.write('var pda { i in {1000..'+str(num_dep)+'},j in {100..'+str(num_arr)+'} : i<>j} integer <=1;\n\n')
  

#############################################################################################################
###########################Headway constraints are written in the model file here############################
#############################################################################################################
  
def headway(headway_list):
  global headwaynum
  global con
  for l in headway_list:
    s='set headway'+str(headwaynum)+'={'
    for x in range(len(l)-1):
      s=s+str(l[x])+','
    s=s+str(l[-1])+'};\n'
    mod.write(s)
    mod.write('subject to con'+str(con)+'{i in headway'+str(headwaynum)+',j in headway'+str(headwaynum)+':i<>j}:(dep[j]-dep[i]+180*pd[i,j])>=3;\n') 
    con=con+1
    mod.write('subject to con'+str(con)+'{i in headway'+str(headwaynum)+',j in headway'+str(headwaynum)+':i<>j}:(dep[j]-dep[i]+180*pd[i,j])<=177;\n\n')
    con=con+1
    headwaynum=headwaynum+1
   
    
#############################################################################################################
##Symmetry constraints are written into the model file in this function######################################
#############################################################################################################    
    
def symmetry(symmetry_list):
  global symmetrynum
  global con
  for l in symmetry_list:
    s='set symmetry'+str(symmetrynum)+'={'
    for x in range(len(l)-1):
      s=s+str(l[x])+','
    s=s+str(l[-1])+'};\n'
    mod.write(s)
    mod.write('subject to con'+str(con)+'{i in symmetry'+str(symmetrynum)+',j in symmetry'+str(symmetrynum)+':i<>j}:(dep[j]-dep[i]+180*pd[i,j])>='+str(int(180/len(l))-3)+';\n') 
    con=con+1
    mod.write('subject to con'+str(con)+'{i in symmetry'+str(symmetrynum)+',j in symmetry'+str(symmetrynum)+':i<>j}:(dep[j]-dep[i]+180*pd[i,j])<='+str(180-int(180/len(l))+3)+';\n\n')
    con=con+1
    symmetrynum=symmetrynum+1  

#############################################################################################################
##Dwell time constraints are written into the model file in this function####################################
############################################################################################################# 
    
def dwell(dwell_list):
  global dwellnum
  global con
  for l in dwell_list:
    s='set dwell'+str(dwellnum)+'={'
    for x in range(len(l)-1):
      s=s+str(l[x])+','
    s=s+str(l[-1])+'};\n'
    mod.write(s)
    mod.write('subject to con'+str(con)+'{i in dwell'+str(dwellnum)+'}:(dep[i+901]-arr[i]+180*pda[i+901,i])>=0.5;\n') 
    con=con+1
    mod.write('subject to con'+str(con)+'{i in dwell'+str(dwellnum)+'}:(dep[i+901]-arr[i]+180*pda[i+901,i])<=1;\n\n')
    con=con+1
    dwellnum=dwellnum+1    

#############################################################################################################
##Traversal constraints are written into the model file in this function#####################################
############################################################################################################# 

def traversal(trav_list,time):
  global travnum
  global con
  for l in trav_list.keys():
    s='set traversal'+str(travnum)+'={'
    for y in range(len(trav_list[l])-1):
      s=s+str(trav_list[l][y])+','     
    s=s+str(trav_list[l][-1])+'};\n'
    mod.write(s)
    mod.write('subject to con'+str(con)+'{i in traversal'+str(travnum)+'}:(arr[i-900]-dep[i]+180*pad[i-900,i])='+str(time[l])+';\n\n') 
    con=con+1
    #mod.write('subject to con'+str(con)+'{i in traversal'+str(travnum)+'}:(arr[i-900]-dep[i]+180*pad[i-900,i])<='+str(time[l]+2)+';\n\n') 
    #con=con+1
    travnum=travnum+1
    
#############################################################################################################
##Turnaround constraints are written into the model file in this function####################################
############################################################################################################# 
    
def turnaround(turnaround_list_arr,turnaround_list_dep,platforms,uplist,downlist):
  global turnaroundnum
  global con
  up = [y for x in uplist.values() for y in x]
  down = [y for x in downlist.values() for y in x]
  print up
  for l in turnaround_list_dep.keys():
    s='set turnaround'+str(turnaroundnum)+'={'
    for y in range(len(turnaround_list_arr[l])-1):
      if turnaround_list_arr[l][y] not in up and turnaround_list_arr[l][y] not in down:
        s=s+str(turnaround_list_arr[l][y])+',' 
    if turnaround_list_arr[l][-1] not in up and turnaround_list_arr[l][-1] not in down:   
      s=s+str(turnaround_list_arr[l][-1])+'};\n'
    mod.write(s)
    turnaroundnum=turnaroundnum+1
    s='set turnaround'+str(turnaroundnum)+'={'
    for y in range(len(turnaround_list_dep[l])-1):
      if turnaround_list_dep[l][y] not in up and turnaround_list_dep[l][y] not in down:
        s=s+str(turnaround_list_dep[l][y])+','   
    if turnaround_list_dep[l][-1] not in up and turnaround_list_dep[l][-1] not in down:     
      s=s+str(turnaround_list_dep[l][-1])+'};\n'
    mod.write(s)  
    mod.write('var st'+str(turnaroundnum-1)+'{i in turnaround'+str(turnaroundnum)+', j in turnaround'+str(turnaroundnum-1)+':i<>j} binary;\n')
    mod.write('subject to con'+str(con)+'{i in turnaround'+str(turnaroundnum)+'}:sum{j in turnaround'+str(turnaroundnum-1)+'}st'+str(turnaroundnum-1)+'[i,j]=1;\n')
    con=con+1
    mod.write('subject to con'+str(con)+'{j in turnaround'+str(turnaroundnum-1)+'}:sum{i in turnaround'+str(turnaroundnum)+'}st'+str(turnaroundnum-1)+'[i,j]=1;\n')
    con=con+1
    mod.write('subject to con'+str(con)+'{i in turnaround'+str(turnaroundnum)+',j in turnaround'+str(turnaroundnum-1)+':i<>j}:(dep[i]-arr[j]+180*pda[i,j])>=3*st'+str(turnaroundnum-1)+'[i,j];\n')
    con=con+1
    #if platforms[int(l)]>2:
    mod.write('subject to con'+str(con)+'{i in turnaround'+str(turnaroundnum)+',j in turnaround'+str(turnaroundnum-1)+':i<>j}:(dep[i]-arr[j]+180*pda[i,j])<='+str(platforms[int(l)]+180)+'-180*st'+str(turnaroundnum-1)+'[i,j];\n\n')
    con=con+1
    #else:
     # mod.write('subject to con'+str(con)+'{i in turnaround'+str(turnaroundnum)+',j in turnaround'+str(turnaroundnum-1)+':i<>j}:(dep[i]-arr[j]+60*pda[i,j])<=65-60*st'+str(turnaroundnum-1)+'[i,j];\n\n')
      #con=con+1  
    turnaroundnum=turnaroundnum+1

#############################################################################################################
##Function creates linkage constraints for services using both up and down tracks############################
#############################################################################################################

def linkage(uplist,downlist):
  global con
  for x in uplist.keys():
     mod.write('subject to con'+str(con)+':(dep['+str(uplist[x][0])+']-arr['+str(downlist[x][0])+']+60*pda['+str(uplist[x][0])+','+str(downlist[x][0])+'])>=2;\n')
     con=con+1
     mod.write('subject to con'+str(con)+':(dep['+str(uplist[x][0])+']-arr['+str(downlist[x][0])+']+60*pda['+str(uplist[x][0])+','+str(downlist[x][0])+'])<=5;\n\n')
     con=con+1   

def find_link_start(down, y):

  for line in down[1:]:
   
    l = line.split(',')[:-1]
    start = [l[x] for x in range(len(l)) if l[x] != '']
    start = start[0]
    l=l[::-1]
    end = [l[x] for x in range(len(l)) if l[x] != ''] 
    end = end[0]

    if int(end) == y:

      ind = l[::-1].index(start)
      return start, down[0].split(',')[ind][:-1]

def arr_dep_list(u, d, l_u, l_d):
  header_up, header_down = u[0].split(',')[:-1], d[0].split(',')[:-1]
  l_u, l_d = l_u.values(), l_d.values()
  l_u = [item for sublist in l_u for item in sublist] 
  l_d = [item for sublist in l_d for item in sublist]
  for line in u[1:]:
    l = line.split(',')[:-1]
    start = [l[x] for x in range(len(l)) if l[x] != '']
    start = start[0]
    l_rev=l[::-1]
    end = [l_rev[x] for x in range(len(l)) if l_rev[x] != '']
    end = end[0] 
    if int(start) in l_u:
      ind = l_u.index(int(start))
      start, st_dep = find_link_start(d,l_d[ind])
      st_arr = header_up[l.index(end)][:-1]
    elif int(start) in l_d:
      ind = l_d.index(start)
      start, st_dep = find_link_start(u, 301)
      st_arr = header_up[l.index(end)][:-1]
    else:
      st_dep = header_up[l.index(start)][:-1]
      st_arr = header_up[l.index(end)][:-1]
    rakelist.write(st_dep+","+st_arr+","+start+","+end+"\n")

  for line in d[1:]:
    l = line.split(',')[:-1]
    start = [l[x] for x in range(len(l)) if l[x] != '']
    start = start[0]
    l_rev=l[::-1]
    end = [l_rev[x] for x in range(len(l)) if l_rev[x] != '']
    end = end[0] 
    if int(start) not in l_u and int(start) not in l_d and int(end) not in l_u and int(end) not in l_d:
      st_dep = header_down[l.index(start)][:-1]
      st_arr = header_down[l.index(end)][:-1]
      rakelist.write(st_dep+","+st_arr+","+start+","+end+"\n")

def occupancy(st_name):
  occu.write(','+st_name+'\n')
  up_arr = up[st_name+'a'].dropna(how='all').tolist()
  down_arr = down[st_name+'a'].dropna(how='all') .tolist()
  up_dep = up[st_name+'d'].dropna().tolist()
  down_dep = down[st_name+'d'].dropna().tolist()
  arr = up_arr + down_arr
  dep = up_dep + down_dep 
  for ind in range(len(arr)):
    occu.write(','+str(int(arr[ind]))+','+str(int(dep[ind]))+',\n')
      
          
#############################################################################################################
##This is the part of the program that accesses different lists that have been created by the other main#####
##program####################################################################################################
#############################################################################################################     
up, down = open('up.csv', 'r'), open('down.csv', 'r')
up_lines, down_lines = up.readlines(), down.readlines()
mod=open('model.mod','w')
num_arr,num_dep=preprocess.a,preprocess.d
headwaylist_up=preprocess.headway_list_up.values()
headwaylist_down=preprocess.headway_list_down.values()
symmetry_up=preprocess.frequency_list_up.values()
symmetry_down=preprocess.frequency_list_down.values()
dwell_up=preprocess.dwell_list_up.values()
dwell_down=preprocess.dwell_list_down.values()
trav_up=preprocess.traversal_dep_up_list
travtime=preprocess.odtrav
trav_down=preprocess.traversal_dep_down_list
turnaround_arr=preprocess.turnaround_arr_list
turnaround_dep=preprocess.turnaround_dep_list
numplat=preprocess.st_numplatform
link_up=preprocess.link_uplist
link_down=preprocess.link_downlist
#############################################################################################################
##Different functions are called by this part of the program that essentially write model files. Objective###
##Function is written down in this part. Apart from this this part also puts comments in the model file######
##for better readability of the same.########################################################################
#############################################################################################################

variables()
mod.write('#Objective function\nminimize cost : 0;\n\n')
mod.write('#Headway constraints\n\n') 
headwaynum=1
symmetrynum=1
dwellnum=1
travnum=1
turnaroundnum=1
con=1
headway(headwaylist_up)
headway(headwaylist_down)
mod.write('#Symmetry constraints\n\n')
symmetry(symmetry_up)
symmetry(symmetry_down)
mod.write('#Dwell Time constraints\n\n')
dwell(dwell_up)
dwell(dwell_down)
mod.write('#Traversal Time constraints\n\n')
traversal(trav_up,travtime)
traversal(trav_down,travtime)
mod.write('#Turnaround constraints\n\n')
turnaround(turnaround_arr,turnaround_dep,numplat,link_up,link_down)
mod.write('#Linkage constraints\n\n')
linkage(link_up,link_down)
up.close(), down.close()

rakelist = open('rakelinks.csv','w')
arr_dep_list(up_lines, down_lines, link_up, link_down)
rakelist.close()
occu = open('Station_occupancy.csv', 'w')
up, down = pd.read_csv('up.csv'), pd.read_csv('down.csv')
for ind in range(len(preprocess.station_list)):
  occupancy(preprocess.station_list[ind])
occu.close()

