#
# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Nokia Corporation - initial contribution.
#
# Contributors:
#
# Description: 
#

import os.path
import sys
import logging
from optparse import OptionParser, OptionGroup
import cone_common
from cone.public import api
from conesub_merge import get_active_root_if_necessary
                          
VERSION = '1.0'

logger    = logging.getLogger('cone')

def find_variant_layers(source_config):
    """
    Find all layers in the configuration that contain custvariant* in
    their path name and return a list containing source->target mappings.
    @param target_config: The target configuration object.
    @param new_name: The new name to replace custvariant* in the
        target path name with.
    @return: A list of (source_layer, target_layer) tuples.
    """
    import re
    pattern = re.compile(r'.*/(custvariant[^/]*)/.*')
    result = []

    for src in source_config.list_configurations():
        m = pattern.match(src)
        if m:
            result.append(src)

    return result
    
def get_metadatas_as_convertprojectml(configuration_root):
    """
    Creates lines for convertprojectml file from metadata items and also adds
    version info to correct place. 
    @param configuration_root: Configuration root object which contains metadatas.
    @return: A list of metadata lines for convertprojectml.
    """
    metadatas = configuration_root.get_meta()
    metadata_line = []
    
    for metadata in metadatas:
        if metadata.ns == "http://www.s60.com/xml/confml/1" or metadata.ns == "http://www.s60.com/xml/confml/2":
            if metadata.tag == 'release':
                metadata_line.append("<" + metadata.tag + ">${convertproject.versioninfo}</" + metadata.tag + ">")
            elif metadata.tag == 'version':
                metadata_line.append("<" + metadata.tag + ">001</" + metadata.tag + ">")
            else:
                metadata_line.append("<" + metadata.tag + ">" + metadata.value + "</" + metadata.tag + ">")

        if metadata.ns == "http://www.nokia.com/xml/cpf-id/1":
            if metadata.attrs['name'] == 'sw_version':
                metadata_line.append('<cv:configuration-property name="' + metadata.attrs['name'] + '" value="${convertproject.versioninfo}" />')
            else:
                metadata_line.append('<cv:configuration-property name="' + metadata.attrs['name'] + '" value="' + metadata.attrs['value'] + '" />')

    return metadata_line
    
def get_layerlist_as_convertprojectml(configuration_root):
    """
    Creates lines for convertprojectml file from layer items in configurtion root
    @param configuration_root: Configuration root object
    @return: A list of layer lines for convertprojectml.
    """
    layer_line = []
    layer_list = configuration_root.list_configurations()
    
    for layer_item in layer_list:
        layer_line.append('<filter action="include_layer" data="' + layer_item + '"/>')
    
    return layer_line
    
def create_convertprojectml_file(conf_filename, conv_proj_filename, filepath,layerlist, metadatas, root_name):
    """
    Creates convertprojectml file for package
    @param conf_filename: configuration root filename
    @param conv_proj_filename: convertprojectml filename
    @param filepath: path where file is created
    @param layerlist: list of layers 
    @param metadatas: list of metadatas
    @param root_name: Name of configuration root
    """

    file_header = ['<?xml version="1.0" encoding="UTF-8"?>' + "\r\n", 
                   '<convertprojectml xmlns="http://www.s60.com/xml/convertprojectml/1">' + "\r\n\r\n",
                   '<targetProject path=""/>' + "\r\n",
                   "\t" + '<layer path="">' + "\r\n",
                   "\t\t" + '<file type="configuration_root" path="'+ conf_filename + '"']

    #root_name can be empty so we need to check is there some content.
    if root_name:
        file_header.append(' configuration_name="' + root_name + '"')                 
    file_header.append('>' + "\r\n")

    file_footer = ["\t\t" + '</file>' + "\r\n",
                   "\t" +'</layer>' + "\r\n",
                   '</convertprojectml>'+ "\r\n"]

    convert_file =  os.path.abspath(filepath + "/" + conv_proj_filename)
    fh = open(convert_file,'wb')
    fh.writelines(file_header)
    fh.write("\t\t\t" + '<meta xmlns:cv="http://www.nokia.com/xml/cpf-id/1">' + "\r\n")

    for meta_line in metadatas:
        fh.write("\t\t\t\t" + meta_line + "\r\n")
    fh.write("\t\t\t" + '</meta>' + "\r\n")

    for layer_line in layerlist:
        fh.write("\t\t\t" + layer_line + "\r\n")

    fh.writelines(file_footer)
    fh.close()
    
def main(argv=sys.argv):
    """ Pack (zip) the variant layers of a configuration. """
    parser = OptionParser(version="%%prog %s" % VERSION)
    
    parser.add_options(cone_common.COMMON_OPTIONS)

    parser.add_option("-p", "--project",
                       dest="project",
                       help="Defines the location of current project. Default is the current working directory.",
                       default=".",
                       metavar="STORAGE")
    
    group = OptionGroup(parser, "Packvariant options",
                        "The packvariant action is intended for packing variant to a zip-file for integration purposes.")
    
    group.add_option("-c", "--configuration",
                        dest="configuration",
                        help="Name of the configuration wanted to be packed.",
                        metavar="CONFIG")
    
    group.add_option("-r", "--remote",
                        dest="remote",
                        help="Defines a location and a name of remote storage (ZIP)",
                        metavar="STORAGE")
    
    group.add_option("-l", "--convert-location",
                        dest="convertlocation",
                        help="Defines a location of convertprojectml file."
                        "Default location is <PROJECT>/convertpluginlayer/implml/",
                        default="/convertpluginlayer/implml/")
        
    parser.add_option_group(group)
    (options, _) = parser.parse_args(argv)
    
    cone_common.handle_common_options(options)
    
    # Check the passed options
    if not options.remote:      parser.error("Target where variant package is placed must be given")
    if not options.configuration:  parser.error("Configuration root to be packed must be given")
    
    try:
        target_storage = api.Storage.open(options.remote,'w', username=options.username, password=options.password)
        target_project = api.Project(target_storage)
        source_storage = api.Storage.open(options.project,'r', username=options.username, password=options.password)
 
        if not os.path.isdir(source_storage.get_path()):
            print "ERROR: --Project must be a directory. Terminating the program."
            sys.exit(1)
        
        source_project = api.Project(source_storage)
        source_config = get_active_root_if_necessary(source_project, options.configuration, 'source')
        source_config = source_project.get_configuration(source_config)
        fname, _ = os.path.splitext(options.configuration)
        conv_project_filename =  fname + ".convertprojectml"
        print "Packing configuration: %s" % options.configuration
        print "Source project: %s" % options.project
        print "Target project: %s" % options.remote
        
        # Adding all files in layers
        layer_list = find_variant_layers(source_config)

        for add_layer in layer_list:
            layer_config = source_project.get_configuration(add_layer)
            layer = layer_config.get_layer()
            path_part = layer.path + "/"
            target_project.import_configuration(layer_config)
            resource_list = layer_config.layer.list_all_resources(recurse=True)

            for single_resource in resource_list:
                parsed_path = path_part + single_resource
                if source_storage.is_resource(parsed_path):
                    logger.info("Adding file: %s" % parsed_path)
                    target_storage.import_resources([parsed_path], source_storage)
                if source_storage.is_folder(path_part + single_resource):
                    logger.info("Adding folder: %s" % parsed_path)
                    target_storage.create_folder(parsed_path)

        layer_list = get_layerlist_as_convertprojectml(source_config)
        metadata_list = get_metadatas_as_convertprojectml(source_config)
        if not source_storage.is_folder(os.path.normpath(options.convertlocation + "/")):
            source_storage.create_folder(os.path.normpath(options.convertlocation + "/"))
        create_convertprojectml_file(options.configuration,
                                     conv_project_filename,
                                     options.project + "/"+ options.convertlocation + "/" ,
                                     layer_list,
                                     metadata_list,
                                     source_config.get_name())

        target_storage.import_resources([os.path.normpath(options.convertlocation + "/" +conv_project_filename)], source_storage) 
    except Exception ,e:
        print "Could not create Zip archive: %s" % e
        sys.exit(2)

    try:
        target_storage.save()
        source_project.close()
        target_project.close()
        
        conv_path = (os.path.normpath(options.project + "/" + options.convertlocation + "/"))
        conv_file_path = (os.path.normpath(conv_path + "/" + conv_project_filename))
        
        os.remove(conv_file_path)
        os.removedirs(conv_path)
    except:
        pass  
        


if __name__ == "__main__":
    main()
