import numpy as np
import random
from random import randint
import math


######################################################################
# for new implemtation, where driver locations are randomly generated
######################################################################

def calculateDis(x1, y1, x2, y2):

    distance_X = (x1 - x2)**2
    distance_Y = (y1 - y2)**2
    distance = math.sqrt(distance_X + distance_Y)
    return distance

def readfile_1(file1):

    Infile = open("./SolomonInstance/"+file1, "r")
    aline = Infile.readline()  # read the 1 st line : item names
    Coor = np.zeros((101, 2))  # 101 nodes: one depot + 100 customers
    for i in range(101):
        aline = Infile.readline()
        values = aline.split()
        Coor[i,0] = float(values[1])      #XCoor
        Coor[i,1] = float(values[2])      #YCoor

    Infile.close()
    return Coor

def readfile_2(file2, PriNum, Coor):

    Infile = open("./SolomonInstance/"+file2, "r")

    # Read data
    aline = Infile.readline()  # read the 1st line: file name
    aline = Infile.readline()  # read the 2nd line: vehicle number (i.e., job number)
    values = aline.split()
    JobNum = int(values[1])

    Route = []
    Dist = []

    # routes
    for i in range(JobNum):
        aline = Infile.readline()
        values = aline.split()
        values_int = [eval(i) for i in values]
        Route.append(values_int)

    # travel distance of each route
    for i in range(JobNum):
        aline = Infile.readline()
        values = aline.split()
        compDist = float(values[1])
        """ don't account the distance from the last node to depot """
        last = Route[i][-2]
        dist = compDist - calculateDis(Coor[0,0], Coor[0,1], Coor[last,0], Coor[last,1])
        Dist.append(dist)

    Dist = np.array(Dist)
    """
    Calculate the bidding set. At the time the decision-maker decides the price, he doesn't know the status 
    of ad-hoc couriers, i.e., we decide the price without using the data of ad-hoc driers
    """

    MinPri = Dist.min()*0.8
    MaxPri = Dist.max()*1.2

    StepPrice = (MaxPri - MinPri) / (PriNum - 1)  # the interval of price is N-1
    price = np.zeros(PriNum)
    for p in range(PriNum):
        val = MinPri + p * StepPrice
        price[p] = (int(val))
    priceRatio = round(price[-1]/price[0],2)

    Infile.close()

    return JobNum, Route, Dist, price, priceRatio

def CourierData(Coor, JobNum, K_t, Dist):

    minCor = [min(idx) for idx in zip(*Coor)]
    maxCor = [max(idx) for idx in zip(*Coor)]
    CoorCourier = np.zeros((K_t,2))         # the coord of each courier
    DistCourier = np.zeros((K_t,JobNum))   # the distance that each courier has to travel when taking a job

    for k in range(K_t):
        CoorCourier[k,0] = randint(minCor[0], maxCor[0])
        CoorCourier[k,1] = randint(minCor[1], maxCor[1])
        for j in range(JobNum):
            DistCourier[k,j] = Dist[j] + calculateDis(Coor[0,0], Coor[0,1], CoorCourier[k,0], CoorCourier[k,1])

    return CoorCourier, DistCourier

# Diatance based bidding: each driver set price and preferred jobs under each price
def CourierSelectJobs(K_t, JobNum, PriNum, BidPrice, DistCourier, price):

    driver_price = np.zeros((K_t, BidPrice))       # the prices selected by a courier
    driver_job_price = np.zeros((K_t, JobNum))     # "driver - job" price
    NoJob = int(JobNum/BidPrice)

    for k in range(K_t):
        # selectedPrice = [] # if a price is selected, we cannot select it again
        sortedDis = np.array(sorted(DistCourier[k,:]))
        sortedInd = np.argsort(DistCourier[k,:])
        # print(DistCourier[k,:], '\n', sortedDis, '\n', sortedIndex, '\n', price, '\n')
        for j in range(BidPrice):
            jobs = []      # the selected jobs (their index) under each bidding price
            if j != BidPrice-1:
                left = j*NoJob
                right = (j+1)*NoJob
                averageDist = sortedDis[left:right].mean()
                jobs.extend(sortedInd[left:right])
            else:
                left = j*NoJob
                averageDist = sortedDis[left:].mean()
                jobs.extend(sortedInd[left:])
            # print(averageDist)
            """
            select price: the one that has the minimal absolute deviation
            """
            absDev = 10000
            indePri = 0
            for p in range(PriNum):
                # if p not in selectedPrice:
                diff = abs(averageDist-price[p])
                if diff <= absDev:
                    absDev = diff
                    indePri = p
            driver_price[k,j] = price[indePri]
            # selectedPrice.append(indePri)
            for l in range(len(jobs)):
                index = jobs[l]
                driver_job_price[k,index] = price[indePri]
        # print(driver_price[k], '\n', driver_job_price[k,:])
    return driver_job_price

# Random bidding: each driver set price and preferred jobs under each price
def CourierSelectJobs_Random(K_t, JobNum, PriNum, BidPrice, price):

    driver_job_price = np.zeros((K_t, JobNum))     # "driver - job" price

    for k in range(K_t):
        Index_Price = random.sample(range(0, PriNum), BidPrice)  # randomly select 'BidPrice' prices (their indices)
        Selected_Price = [price[Index_Price[i]] for i in range(BidPrice)]
        for j in range(JobNum):   # for each job, set the courier's preferred price
            Bid_or_not = random.randint(0,1)
            if Bid_or_not == 0:     # the courier is not interested in the job
                driver_job_price[k,j] = price[PriNum-1]     # the bidding price is P_N
            else:
                if BidPrice == 1:
                    driver_job_price[k,j] = Selected_Price[0]
                else:
                    Index = random.randint(0, BidPrice-1)
                    driver_job_price[k,j] = Selected_Price[Index]
    return driver_job_price

