import numpy as np
from operator import itemgetter
from random import shuffle
from cylp.cy import CyCoinIndexedVector
import random
from math import floor
from cylp.py.pivots import PositiveEdgePivot


class PositiveEdgeWolfePivot(PositiveEdgePivot):

    def __init__(self, clpModel, bucketSize=1, EPSILON=10 ** (-7)):
        PositiveEdgePivot.__init__(self, clpModel, bucketSize, EPSILON)
        self.banList = []

    def pivotColumnFirst(self, updates, spareRow1, spareRow2, spareCol1, spareCol2):
        '''Finds the first variable with an acceptable
        reduced costs searching in random buckets
        '''
        self.updateReducedCosts(updates, spareRow1, spareRow2, spareCol1, spareCol2)
        s = self.clpModel
        rc = s.getReducedCosts()
        dim = s.getNumRows() + s.getNumCols()
        nRows = s.getNumRows()

        tol = s.dualTolerance()

        if not self.initialized:
            self.initialize()

        xranges = self.xranges
        numberOfSegments = self.numberOfSegments
        segmentSize = self.segmentSize

        shuffle(xranges)

        bestFreeVarInd = -1
        bestVarInd = -1
        bestCompVarInd = -1

        bestFreeVarRC = 0
        bestVarRC = 0
        bestCompVarRC = 0

        goodVariableFound = 0

        for iSegment in range(numberOfSegments):
#           comp_ind = [i for i in range(dim) if not s.flagged(i) and \
#                     (rc[i] < -tol and \
#                     s.getStatus(i) == 3 and \
#                     self.isCompatible(i)) \
#                     or \
#                     (rc[i] > tol and \
#                     s.getStatus(i) == 2 and \
#                     self.isCompatible(i)) \
#                     or \
#                     (abs(rc[i]) > tol and \
#                     (s.getStatus(i) == 4 or s.getStatus(i) == 0) and \
#                     self.isCompatible(i))]

            for i in ranges[iSegment]:
                if s.flagged(i) or \
                            abs(rc[i]) < self.EPSILON or \
                            i in self.banList:
                    continue
                if s.getStatus(i) == 3:  # at lowerbound
                    if rc[i] < bestCompVarRC and self.isCompatible(i):
                        bestCompVarRC = rc[i]
                        bestCompVarInd = i
                        goodVariableFound += 1
                    if rc[i] < bestVarRC:
                        bestVarRC = rc[i]
                        bestVarInd = i
                        goodVariableFound += 1

                elif s.getStatus(i) == 2:  # at upperbound
                    r = -rc[i]
                    if r < bestCompVarRC and self.isCompatible(i):
                        bestCompVarRC = r
                        bestCompVarInd = i
                        goodVariableFound += 1
                    if r < bestVarRC:
                        bestVarRC = r
                        bestVarInd = i
                        goodVariableFound += 1
                # Free or Superbasic
                elif s.getStatus(i) == 0 or s.getStatus(i) == 4:
                    r = -abs(rc[i])
                    if r < bestCompVarRC and self.isCompatible(i):
                        bestCompVarRC = r
                        bestCompVarInd = i
                        goodVariableFound += 1
                    if r < bestFreeVarRC:
                        bestFreeVarRC = r
                        bestFreeVarInd = i
                        goodVariableFound += 1

            if goodVariableFound > 0:
                break

        l = [bestVarRC, bestFreeVarRC, bestCompVarRC]

        needUpdate = True
        if bestVarInd != -1:
            if bestFreeVarInd != -1:
                if 0.1 * bestVarRC > bestFreeVarRC:
                    bestVarRC = bestFreeVarRC
                    bestVarInd = bestFreeVarInd

            if bestCompVarInd != -1:
                if 0.4 * bestVarRC > bestCompVarRC:
                    bestVarRC = bestCompVarRC
                    bestVarInd = bestCompVarInd
                    needUpdate = False

        elif bestFreeVarInd != -1:
            if bestCompVarInd != -1 and 0.4 * bestFreeVarRC > bestCompVarRC:
                bestVarRC = bestCompVarRC
                bestVarInd = bestCompVarInd
                needUpdate = False
            else:
                bestVarRC = bestFreeVarRC
                bestVarInd = bestFreeVarInd

        if needUpdate:
            self.updateP()
            self.updateW()

        return bestVarInd

    def saveWeights(self, model, mode):
        self.clpModel = model

    def isPivotAcceptable(self):
        #TODO ComplementarityList can be defined in the current class
        s = self.clpModel
        cl = s.getComplementarityList()
        pivotRow = s.pivotRow()
        if pivotRow < 0:
            return 1

        pivotVariable = s.getPivotVariable()
        leavingVarIndex = pivotVariable[pivotRow]
        colInd = s.sequenceIn()

        if s.getStatus(cl[colInd]) == 1 and \
            cl[colInd] != leavingVarIndex:
            self.banList.append(colInd)

            #s.setFlagged(colInd)
            return 0

        #self.banList = np.zeros(self.dim, np.int)
        self.banList = []

        return 1
