import os

import utils
import config

from java.lang import RuntimeException
from java.io import File
from java.util import Properties

from ch.systemsx.cisd.common.fileconverter import FileConverter, Tiff2PngConversionStrategy
from ch.systemsx.cisd.openbis.generic.shared.basic.dto.api import ValidationException

from ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.transformations import ImageTransformationBuffer 
from ch.systemsx.cisd.openbis.dss.etl.dto.api.v1 import SimpleImageDataConfig, ImageMetadata, OriginalDataStorageFormat, Location 
from ch.systemsx.cisd.openbis.dss.etl.custom.geexplorer import GEExplorerImageAnalysisResultParser
from ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto import Geometry

#reload(config)

class MyImageDataSetConfig(SimpleImageDataConfig):
    
    def __init__(self, incomingDir):
        self.incomingDir = incomingDir
        self.setStorageConfiguration()
        
    def setStorageConfiguration(self):
        self.setStoreChannelsOnExperimentLevel(config.STORE_CHANNELS_ON_EXPERIMENT_LEVEL)
        self.setOriginalDataStorageFormat(config.ORIGINAL_DATA_STORAGE_FORMAT)
        if config.GENERATE_THUMBNAILS:
            self.setGenerateThumbnails(True)
            #self.setUseImageMagicToGenerateThumbnails(True)
            self.setAllowedMachineLoadDuringThumbnailsGeneration(config.ALLOWED_MACHINE_LOAD_DURING_THUMBNAIL_GENERATION)
            self.setMaxThumbnailWidthAndHeight(config.MAX_THUMNAIL_WIDTH_AND_HEIGHT)
    
    """
    Creates ImageFileInfo for a given ImageTokens.
    Converts tile number to coordinates on the 'well matrix'.
    Example file name: A - 1(fld 1 wv Cy5 - Cy5).tif
    Returns:
        ImageTokens
    """
    def extractImageMetadata(self, path):
        imageTokens = ImageMetadata()
        
        imageFile = File(self.incomingDir, path)
        if not self.incomingDir.equals(imageFile.getParentFile()):
            return None
    
        basename = os.path.splitext(imageFile.name)[0]
        wellText = basename[0:utils.find(basename, "(")] # A - 1
        imageTokens.well = wellText.replace(" - ", "")
        
        if " wv " in basename:
            fieldText = basename[utils.find(basename, "fld ") + 4 : utils.find(basename, " wv")]
            imageTokens.channelCode = basename[utils.rfind(basename, " - ") + 3 :-1]
        else:
            fieldText = basename[utils.find(basename, "fld ") + 4 : utils.find(basename, ")")]
            imageTokens.channelCode = "DEFAULT"
        
        try:
            imageTokens.tileNumber = int(fieldText)
        except ValueError:
            raise ValidationException("Cannot parse field number from '" + fieldText + "' in '" + basename + "' file name.")
    
        return imageTokens
    
    def getTileCoordinates(self, tileNumber, tileGeometry):
        columns = tileGeometry.getWidth()
        row = ((tileNumber - 1) / columns) + 1
        col = ((tileNumber - 1) % columns) + 1
        return Location(row, col)
    
    def getTileGeometry(self, imageMetadataList, maxTile):
        if maxTile % 4 == 0 and maxTile != 4:
            (cols, rows) = (maxTile / 4, 4)
        elif maxTile % 3 == 0:
            (cols, rows) = (maxTile / 3, 3)
        elif maxTile % 2 == 0:
            (cols, rows) = (maxTile / 2, 2)
        else:
            (cols, rows) = (maxTile, 1)
        
        return Geometry.createFromRowColDimensions(cols, rows);
   
    def getAvailableChannelTransformations(self, channelCode):
        buffer = ImageTransformationBuffer()
        buffer.appendAllBitShiftsFor12BitGrayscale()
        buffer.appendAutoRescaleGrayscaleIntensity(0, "Original contrast")
        return buffer.getTransformations()
        
def createRawImagesDataset(incoming, plate, batchName, transaction, factory):
    imageDatasetConfig = MyImageDataSetConfig(incoming)
    imageDatasetConfig.setRawImageDatasetType()
    imageDatasetConfig.setFileFormatType(config.IMAGE_DATASET_FILE_FORMAT)
    imageDatasetConfig.setUseImageMagicToGenerateThumbnails(config.USE_IMAGE_MAGIC_CONVERT_TOOL)
   
    imageDatasetConfig.setImageLibrary("IJ", "tiff")
    
    #imageDatasetConfig.setComputeCommonIntensityRangeOfAllImagesForChannels(["DAPI"])
    #imageDatasetConfig.setComputeCommonIntensityRangeOfAllImagesIsDefault(False)
    #imageDatasetConfig.setComputeCommonIntensityRangeOfAllImagesForAllChannels()
    #imageDatasetConfig.setComputeCommonIntensityRangeOfAllImagesThreshold(0.01)

    # Available in the next release:
    #imageDatasetConfig.setThumbnailsGenerationImageMagicParams(["-contrast-stretch", "0"])
    imageDatasetDetails = factory.createImageRegistrationDetails(imageDatasetConfig, incoming)
    imageDataSet = transaction.createNewDataSet(imageDatasetDetails)
    imageDataSet.setPropertyValue(config.IMAGE_DATASET_BATCH_PROPCODE, batchName)
    imageDataSet.setSample(plate)
    return imageDataSet

def convertToPng(dir, transparentColor):
    delete_original_files = True
    strategy = Tiff2PngConversionStrategy(transparentColor, 0, delete_original_files)
    # Uses cores * machineLoad threads for the conversion, but not more than maxThreads
    machineLoad = config.ALLOWED_MACHINE_LOAD_DURING_THUMBNAIL_GENERATION
    maxThreads = 100
    errorMsg = FileConverter.performConversion(File(dir), strategy, machineLoad, maxThreads)
    if errorMsg:
        raise RuntimeException("Error converting overlays:" + errorMsg)

def registerSegmentationImages(overlaysDir, plate, imageDataSetCode, analysisProcedureCode, transaction, factory):
    convertToPng(overlaysDir.getPath(), config.OVERLAYS_TRANSPARENT_COLOR)
    overlayDatasetConfig = MyImageDataSetConfig(overlaysDir)
    overlayDatasetConfig.setSegmentationImageDatasetType()
    overlayDatasetConfig.setFileFormatType(config.OVERLAY_IMAGE_FILE_FORMAT)
    overlayDatasetConfig.setUseImageMagicToGenerateThumbnails(config.USE_IMAGE_MAGIC_CONVERT_TOOL)
    overlayDatasetConfig.setGenerateHighQualityThumbnails(True)
    overlayDatasetConfig.setImageLibrary("IJ", "tiff")
    # Available in the next release:
    #overlayDatasetConfig.setThumbnailsGenerationImageMagicParams(["-contrast-stretch", "0"])

    overlayDatasetDetails = factory.createImageRegistrationDetails(overlayDatasetConfig, overlaysDir)
    if analysisProcedureCode:
        overlayDatasetDetails.setPropertyValue("$ANALYSIS_PROCEDURE", analysisProcedureCode)
    overlayDataset = transaction.createNewDataSet(overlayDatasetDetails)
    overlayDataset.setSample(plate)
    overlayDataset.setParentDatasets([ imageDataSetCode ])
    transaction.moveFile(overlaysDir.getPath(), overlayDataset, "overlays")

def registerAnalysisData(analysisXmlFile, plate, parentDatasetCode, transaction, factory):
    analysisCSVFile = File(analysisXmlFile.getPath() + ".csv")
    geXmlParser = GEExplorerImageAnalysisResultParser(analysisXmlFile.getPath())
    geXmlParser.writeCSV(analysisCSVFile)
    
    featureProps = Properties()
    featureProps.setProperty("separator", ",")
    featureProps.setProperty("well-name-row", "Well")
    featureProps.setProperty("well-name-col", "Well")
    
    analysisDataSetDetails = factory.createFeatureVectorRegistrationDetails(analysisCSVFile.getPath(), featureProps)
    analysisProcedureCode = geXmlParser.getAnalysisProcedureName()
    analysisDataSetDetails.getDataSetInformation().setAnalysisProcedure(analysisProcedureCode)
    analysisDataSet = transaction.createNewDataSet(analysisDataSetDetails)
    analysisDataSet.setSample(plate)
    analysisDataSet.setParentDatasets([ parentDatasetCode ])
    analysisDataSet.setFileFormatType(config.ANALYSIS_FILE_FORMAT)
    parentDirName = "analysis"
    transaction.createNewDirectory(analysisDataSet, parentDirName)
    transaction.moveFile(analysisCSVFile.getPath(), analysisDataSet, parentDirName)
    transaction.moveFile(analysisXmlFile.getPath(), analysisDataSet, parentDirName)
    return analysisProcedureCode
        
        
""" 
Returns a tuple: 
 (plate sample connected to the dataset with the specified code, experiment connected to the plate) 
Raises exception is no sample is connected to the dataset.
"""
def findConnectedPlate(transaction, datasetCode):
    parentDataset = transaction.getDataSet(datasetCode)
    if not parentDataset:
        raise ValidationException("Cannot find a dataset with code '%s'" % (datasetCode))
    plate = parentDataset.getSample()
    if not plate:
        raise ValidationException("Dataset with code '%s' is not connected to any plate" % (datasetCode))
    
    # fetch the sample again to fetch connected experiment 
    plateIdentifier = plate.getSampleIdentifier()
    plate = transaction.getSample(plateIdentifier)
    plateExperiment = plate.getExperiment()
    if not plateExperiment:
        raise ValidationException("Cannot find experiment of a plate '%s' (connected to dataset with code '%s')" % (plateIdentifier, datasetCode))
    experimentIdent = plateExperiment.getExperimentIdentifier()
    # fetch the experiment again to have all its properties
    experiment = transaction.getExperiment(experimentIdent)

    return (plate, experiment)