#!/usr/bin/python 

from pyCmpl import *
import sys
import os
import shutil
import tempfile



class McpException(Exception):
    pass



#*************** CmplMcp  ************************************************
class Mcp(object):

    #*********** constructor **********
    def __init__(self, nrOfSources, nrOfDests, distCostMatrix, radius, maxSrcCost, demands, LogLabHomePath):
        
        self.__home = LogLabHomePath  
        cmplHomePath =  LogLabHomePath + 'Cmpl' 
        os.environ.update({'CMPLHOME': cmplHomePath })
        
        self.__model = None
   
        self.__fixedCosts = [ 1 for a in range(nrOfSources) ]
        self.__isFixedCosts = False

        self.__demands = demands

        self.__distCostMatrix = distCostMatrix
        self.__maxSrsCost = maxSrcCost
       
        self.__coveringMatrix =  [ [ 0 for i in range(nrOfSources)] for j in range(nrOfDests) ]
        for i in range(nrOfSources):
            for j in range(nrOfDests):
                if distCostMatrix[i][j] <= radius: 
                    self.__coveringMatrix[j][i] = 1
                else:
                    self.__coveringMatrix[j][i] = 0

        self.__nrOfDestinations = nrOfDests 
        self.__nrOfSources = nrOfSources

        self.__totalSupply = 0
        self.__activeSources = [ 0  for a in range(nrOfSources)  ]
        self.__destSourceMappingSingleSource =  [ -1  for a in range(nrOfDests)  ]
        self.__destSourceMapping =  [ []   for a in range(nrOfDests)  ]

        self.solutionStatus = False

        self.__tmpPath = tempfile.gettempdir()+os.sep
        
    #*********** end constructor ******
    

    def setFixedCosts(self, fixedCosts):
        if not isinstance(fixedCosts,list) or len(fixedCosts)!=self.__nrOfSources:
            raise McpException(f'Mcp error: Wrong fixed cost vector ')
        else:
            self.__fixedCosts = fixedCosts  
        self.__isFixedCosts = True

    def getObjValue(self):
        return self.__totalSupply

    def getActiveSources(self):
        return self.__activeSources

    def getDestSourceMappingSingleSource(self):
        return self.__destSourceMappingSingleSource

    def getDestSourceMapping(self):
        return self.__destSourceMapping

    def isSolution(self):
        return self.solutionStatus

    def __checkDemands(self):
        if sum(self.__demands)==0:
            self.__demands = [ 1 for _ in self.__demands]
            

    #*********** solve ************
    def solve(self, timeLimit=300, mipGap=0.01):
        src = CmplSet("Sources")
        src.setValues(1,self.__nrOfSources )
        
        dest = CmplSet("Destinations")
        dest.setValues(1,self.__nrOfDestinations)
        
        a = CmplParameter("a", dest,src )
        a.setValues(self.__coveringMatrix)
                
        f =  CmplParameter("f",src)    
        f.setValues(self.__fixedCosts)

        self.__checkDemands()

        d = CmplParameter("d",dest)    
        d.setValues(self.__demands)

        p =  CmplParameter("p" )
        p.setValues(self.__maxSrsCost)
                                   
        modelName = self.__home + 'CmplApps'+os.sep+'Mcp.cmpl'
        shutil.copyfile(modelName, self.__tmpPath+"Mcp.cmpl") 
        modelName=self.__tmpPath+"Mcp.cmpl" 

        self.__model = Cmpl(modelName)

        self.__model.setSets(src,dest)

        self.__model.setParameters( a, f, d, p )

    
        #self.__model.debug()
        self.__model.setOutput(False)

        #self.__model.setOption('-solver scip')   
        #self.__model.setOption(f'-opt scip limits/time={timeLimit}')   

        self.__model.setOption('-solver highs')   
        self.__model.setOption(f'-opt highs time_limit={timeLimit}')  
               
        #self.__model.debug()
        self.__model.solve()
              
        if self.__model.solverStatus == SOLVER_OK:
            
            self.__totalSupply = self.__model.solution.value

            if self.__model.solution.value == 0:
                  raise McpException('No solution has been found')

            self.solutionStatus = True

            for i in src.values:
                try:
                    if self.__model.x[i].activity == 1:
                        self.__activeSources[i-1] = 1
                except:
                    pass
            
            for j in dest.values:
                for i in src.values:
                    if self.__activeSources[i-1] == 1 and self.__coveringMatrix[j-1][i-1]==1:
                        self.__destSourceMapping[j-1].append(i)
            
                minDist=1000000000
                minIdx=-1
                for i in self.__destSourceMapping[j-1]:
                    if self.__distCostMatrix[i-1][j-1]<minDist:
                        minDist=self.__distCostMatrix[i-1][j-1]
                        minIdx=i
                self.__destSourceMappingSingleSource[j-1]=minIdx
        else:
            raise McpException('No solution has been found')
    #*********** end solve **************
    
