/*
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
 * Foundation.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
 * or from the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * Copyright (c) 2011 - 2017 Hitachi Vantara..  All rights reserved.
 */
package org.pentaho.metadata;

import static org.junit.Assert.assertEquals;

import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;

import junit.framework.Assert;

import org.junit.Test;
import org.pentaho.metadata.model.Category;
import org.pentaho.metadata.model.Domain;
import org.pentaho.metadata.model.concept.types.LocalizedString;
import org.pentaho.metadata.util.LocalizationUtil;
import org.pentaho.metadata.util.XmiParser;

/**
 * TODO: Bring sorter over from code
 * 
 * @author gorman
 * 
 */
public class LocalizationUtilIT {

  @Test
  public void testImportExportOfMultibyteChars() throws Exception {
    // 日本語

    XmiParser parser = new XmiParser();
    Domain domain = parser.parseXmi( getClass().getResourceAsStream( "/simple_model.xmi" ) );
    Category category = new Category( domain.getLogicalModels().get( 0 ) );
    category.setId( "TEST_WITH_日本語_CHARS" );
    LocalizedString str = new LocalizedString( "en_US", "日本語" );
    category.setName( str );
    domain.getLogicalModels().get( 0 ).addCategory( category );

    LocalizationUtil util = new LocalizationUtil();
    Properties props = util.exportLocalizedProperties( domain, "en_US" );

    Assert.assertEquals( props.getProperty( "[LogicalModel-BV_MODEL_1].[Category-TEST_WITH_日本語_CHARS].[name]" ),
        "日本語" );

    props.setProperty( "[LogicalModel-BV_MODEL_1].[name]", "日本語" );
    props
        .setProperty( "[LogicalModel-BV_MODEL_1].[Category-TEST_WITH_日本語_CHARS].[name]", "2nd Version 日本語" );

    Assert.assertEquals( "en_US", domain.getLocales().get( 0 ).getCode() );

    util.importLocalizedProperties( domain, props, "jp" );

    Assert.assertEquals( "en_US", domain.getLocales().get( 0 ).getCode() );
    Assert.assertEquals( "jp", domain.getLocales().get( 1 ).getCode() );

    Assert.assertEquals( domain.getLogicalModels().get( 0 ).getName( "jp" ), "日本語" );
    Assert.assertEquals( domain.getLogicalModels().get( 0 ).getCategories().get( 1 ).getName( "jp" ),
        "2nd Version 日本語" );
  }

  @Test
  public void testNonHappyPaths() throws Exception {
    XmiParser parser = new XmiParser();
    Domain domain = parser.parseXmi( getClass().getResourceAsStream( "/simple_model.xmi" ) );
    LocalizationUtil util = new LocalizationUtil();
    Properties props = util.exportLocalizedProperties( domain, "en_US" );

    // invalid references
    props.setProperty( "[Test].[Property]", "Test Value" );
    props.setProperty( "[PhysicalModel-SampleDataDatasource2].[PT_ORDERDETAILS].[name]", "ORDERDETAILS" );
    props.setProperty( "[LogicalModel-BV_MODEL_2].[LogicalTable-BT_ORDERDETAILS_ORDERDETAILS].[name]", "ORDERDETAILS" );
    props.setProperty( "[PhysicalModel-SampleDataDatasource].[PT_ORDERS2].[ORDERNUMBER].[name]", "ORDERNUMBER" );
    props.setProperty( "[LogicalModel-BV_MODEL_1].[Category-2CAT_Category].[name]", "Category" );
    props.setProperty( "[PhysicalModel-SampleDataDatasource].[PT_ORDERDETAILS2].[ORDERLINENUMBER].[name]",
        "ORDERLINENUMBER" );
    props.setProperty( "[Base].[comments2]", "Comment String" );
    props.setProperty( "[PhysicalModel-SampleDataDatasource].[PT_ORDERS].[STATUS2].[name]", "STATUS" );
    props.setProperty( "[LogicalModel-BV_MODEL_1].[LogicalTable2-BT_ORDERS_ORDERS].[name]", "ORDERS" );
    props.setProperty( "[PhysicalModel-SampleDataDatasource].[PT_ORDERDETAILS].[ORDERNUMBER2].[name]", "ORDERNUMBER" );
    props.setProperty( "[LogicalModel-BV_MODEL_1].[name2]", "Model 1" );
    props.setProperty( "[PhysicalModel-SampleDataDatasource].[PT_ORDERS2].[name]", "ORDERS" );
    props.setProperty( "[Base].[Base Child2].[comments]", "Child Comment String" );

    // unparsable strings
    props.setProperty( "[Base].[Ba[[se Child2].[comments]", "Child Comment String" );
    props.setProperty( "[Base].[Base Chi]]ld2].[comments]", "Child Comment String" );
    props.setProperty( "[Base].[Base Chi]]ld2].[comm[]ents]", "Child Comment String" );
    props.setProperty( "[B..[]ase].[Base Chi]]ld2].[comm..ents]", "Child Comment String" );
    props.setProperty( "[Base", "Child Comment String" );
    props.setProperty( "Base", "Child Comment String" );

    List<String> messages = util.analyzeImport( domain, props, "en_TEST" );

    Assert.assertEquals( 19, messages.size() );

    Assert.assertEquals( "en_US", domain.getLocales().get( 0 ).getCode() );

    util.importLocalizedProperties( domain, props, "en_TEST" );

    Assert.assertEquals( "en_US", domain.getLocales().get( 0 ).getCode() );
    Assert.assertEquals( "en_TEST", domain.getLocales().get( 1 ).getCode() );

  }

  @Test
  public void testAnalyzeImport() throws Exception {
    // this test exercises all known places where localized strings are located
    XmiParser parser = new XmiParser();
    Domain domain = parser.parseXmi( getClass().getResourceAsStream( "/simple_model.xmi" ) );
    LocalizationUtil util = new LocalizationUtil();

    Properties props = util.exportLocalizedProperties( domain, "en_US" );

    List<String> messages = util.analyzeImport( domain, props, "en_US" );

    Assert.assertEquals( 0, messages.size() );

    props.remove( "[Base].[comments]" );
    props.setProperty( "[Test].[Property]", "Test Value" );

    messages = util.analyzeImport( domain, props, "en_US" );

    Assert.assertEquals( 2, messages.size() );
    Assert.assertEquals( messages.get( 0 ), "Key [Base].[comments] is missing from imported bundle" );
    Assert.assertEquals( messages.get( 1 ), "Imported key [Test].[Property] is not referenced in domain" );
  }

  @Test
  public void testSimpleModel() throws Exception {
    // this test exercises all known places where localized strings are located
    XmiParser parser = new XmiParser();
    Domain domain = parser.parseXmi( getClass().getResourceAsStream( "/simple_model.xmi" ) );
    LocalizationUtil util = new LocalizationUtil();

    Properties props = util.exportLocalizedProperties( domain, "en_US" );

    // There are 153 externalized strings in steel wheels
    Assert.assertEquals( 19, props.size() );
    for ( Object key : props.keySet() ) {
      System.out.println( key + "=" + props.get( key ) );
    }

    Assert.assertEquals( props.getProperty( "[IPhysicalModel-SampleDataDatasource].[PT_ORDERDETAILS].[name]" ),
        "ORDERDETAILS" );
    Assert.assertEquals( props
        .getProperty( "[LogicalModel-BV_MODEL_1].[LogicalTable-BT_ORDERDETAILS_ORDERDETAILS].[name]" ), "ORDERDETAILS" );
    Assert.assertEquals( props.getProperty( "[IPhysicalModel-SampleDataDatasource].[PT_ORDERS].[ORDERNUMBER].[name]" ),
        "ORDERNUMBER" );
    Assert.assertEquals( props.getProperty( "[LogicalModel-BV_MODEL_1].[Category-CAT_Category].[name]" ), "Category" );
    Assert.assertEquals( props
        .getProperty( "[IPhysicalModel-SampleDataDatasource].[PT_ORDERDETAILS].[ORDERLINENUMBER].[name]" ),
        "ORDERLINENUMBER" );
    Assert.assertEquals( props.getProperty( "[Base].[comments]" ), "Comment String" );
    Assert.assertEquals( props.getProperty( "[IPhysicalModel-SampleDataDatasource].[PT_ORDERS].[STATUS].[name]" ),
        "STATUS" );
    Assert.assertEquals( props.getProperty( "[LogicalModel-BV_MODEL_1].[LogicalTable-BT_ORDERS_ORDERS].[name]" ),
        "ORDERS" );
    Assert.assertEquals( props
        .getProperty( "[IPhysicalModel-SampleDataDatasource].[PT_ORDERDETAILS].[ORDERNUMBER].[name]" ), "ORDERNUMBER" );
    Assert.assertEquals( props.getProperty( "[LogicalModel-BV_MODEL_1].[name]" ), "Model 1" );
    Assert.assertEquals( props.getProperty( "[IPhysicalModel-SampleDataDatasource].[PT_ORDERS].[name]" ), "ORDERS" );
    Assert.assertEquals( props.getProperty( "[Base].[Base Child].[comments]" ), "Child Comment String" );

    util.importLocalizedProperties( domain, props, "en_TEST" );

    Properties newProps = util.exportLocalizedProperties( domain, "en_TEST" );

    Assert.assertEquals( newProps.size(), props.size() );
    for ( Object key : props.keySet() ) {
      String k = (String) key;
      Assert.assertEquals( props.getProperty( k ), newProps.getProperty( k ) );
    }

    String xmi = parser.generateXmi( domain );

    ByteArrayInputStream is = new ByteArrayInputStream( xmi.getBytes() );
    Domain domain2 = parser.parseXmi( is );
    Properties newProps2 = util.exportLocalizedProperties( domain, "en_TEST" );
    Assert.assertEquals( newProps2.size(), props.size() );

    for ( Object key : props.keySet() ) {
      String k = (String) key;
      Assert.assertEquals( props.getProperty( k ), newProps2.getProperty( k ) );
    }

    Assert.assertEquals( "en_US", domain.getLocales().get( 0 ).getCode() );
    Assert.assertEquals( "en_TEST", domain.getLocales().get( 1 ).getCode() );
  }

  @Test
  public void testLocalization() throws Exception {
    XmiParser parser = new XmiParser();
    Domain domain = parser.parseXmi( getClass().getResourceAsStream( "/samples/steelwheels.xmi" ) );
    LocalizationUtil util = new LocalizationUtil();

    //
    // Test exporting
    //

    Properties props = util.exportLocalizedProperties( domain, "en_US" );

    // There are 277 externalized strings in steel wheels
    Assert.assertEquals( 277, props.size() );

    // Spot Checks

    // physical column name
    Assert.assertEquals( props.getProperty( "[IPhysicalModel-SampleData].[PT_EMPLOYEES].[EMAIL].[name]" ), "Email" );
    // category name
    Assert.assertEquals( props.getProperty( "[LogicalModel-BV_ORDERS].[Category-BC_CUSTOMER_W_TER_].[name]" ),
        "Customer" );
    // model name
    Assert.assertEquals( props.getProperty( "[LogicalModel-BV_ORDERS].[name]" ), "Orders" );
    // logical table name
    Assert.assertEquals( props.getProperty( "[LogicalModel-BV_ORDERS].[LogicalTable-BT_PRODUCTS_PRODUCTS].[name]" ),
        "Products" );
    // logical column comments
    Assert
        .assertEquals(
            props
                .getProperty( "[LogicalModel-BV_ORDERS].[LogicalTable-BT_ORDERDETAILS_ORDERDETAILS].[BC_ORDERDETAILS_TOTAL].[comments]" ),
            "This field is computed as Quantity Ordered times Price Sold" );

    //
    // test importing
    //

    util.importLocalizedProperties( domain, props, "en_TEST" );
    Properties newProps = util.exportLocalizedProperties( domain, "en_TEST" );
    Assert.assertEquals( newProps.size(), props.size() );

    for ( Object key : props.keySet() ) {
      String k = (String) key;
      Assert.assertEquals( props.getProperty( k ), newProps.getProperty( k ) );
    }

    //
    // test serializing import
    //

    String xmi = parser.generateXmi( domain );

    ByteArrayInputStream is = new ByteArrayInputStream( xmi.getBytes( "UTF-8" ) );
    Domain domain2 = parser.parseXmi( is );
    Properties newProps2 = util.exportLocalizedProperties( domain, "en_TEST" );
    Assert.assertEquals( newProps2.size(), props.size() );

    for ( Object key : props.keySet() ) {
      String k = (String) key;
      Assert.assertEquals( props.getProperty( k ), newProps2.getProperty( k ) );
    }

    Assert.assertEquals( "en_US", domain.getLocales().get( 0 ).getCode() );
    Assert.assertEquals( "es", domain.getLocales().get( 1 ).getCode() );
    Assert.assertEquals( "en_TEST", domain.getLocales().get( 2 ).getCode() );
  }

  /**
   * Tests the import of localization properties exported from a model that was published from aAgile BI to a BI Server.
   **/
  @Test
  public void testImportPropertiesIntoAgileBiPublishedModel() throws Exception {

    // this test exercises all known places where localized strings are located
    XmiParser parser = new XmiParser();
    Domain domain = null;
    domain = parser.parseXmi( getClass().getResourceAsStream( "/agileBiGenerated.xmi" ) );
    LocalizationUtil util = new LocalizationUtil();

    // Load the properties from the exported properties file
    Properties exportedPropertyFileProps = new Properties();
    exportedPropertyFileProps.load( getClass().getResourceAsStream( "/agileBiGenerated_en_US.properties" ) );

    // import the properties into the domain
    List<String> messages = util.analyzeImport( domain, exportedPropertyFileProps, "en_US" );
    if ( messages.isEmpty() ) {
      Assert.assertTrue( messages.isEmpty() );
    } else {
      for ( String message : messages ) {
        System.out.println( message );
      }
      Assert.fail( "The analysis of the export failed." );
    }
  }

  @Test
  public void testImportedLocaleIntoDomainWithSameLocale() throws Exception {

    // this test exercises all known places where localized strings are located
    String locale = "en_US";
    XmiParser parser = new XmiParser();
    Domain domain = null;
    domain = parser.parseXmi( getClass().getResourceAsStream( "/modelWith_EN_US.xmi" ) );
    LocalizationUtil util = new LocalizationUtil();

    // Load the properties from the exported properties file
    Properties exportedPropertyFileProps = new Properties();
    exportedPropertyFileProps.load( getClass().getResourceAsStream( "/modelWith_EN_US_en_US.properties" ) );

    // import the properties into the domain
    util.importLocalizedProperties( domain, exportedPropertyFileProps, locale );

    // Out imported localization will have the string "en_US" before each non empty string.
    // take the printing of all the properties out before checking in
    Properties en_US_FromDomain = util.exportLocalizedProperties( domain, locale );
    for ( Entry<Object, Object> entry : en_US_FromDomain.entrySet() ) {
      System.out.println( entry.getKey().toString() + " => " + entry.getValue().toString() );
    }

    assertEquals( "en_US Num children at home", en_US_FromDomain
        .get( "[IPhysicalModel-foodmart].[PT_CUSTOMER].[num_children_at_home].[name]" ) );

  }

  /**
   * Tests the import of localization properties exported from a model that was published from aAgile BI to a BI Server.
   **/
  @Test
  public void testImportPropertiesExportedFromPME() throws Exception {

    // this test exercises all known places where localized strings are located
    XmiParser parser = new XmiParser();
    Domain domain = null;
    domain = parser.parseXmi( getClass().getResourceAsStream( "/exportedFromPME.xmi" ) );
    LocalizationUtil util = new LocalizationUtil();

    // Load the properties from the exported properties file
    Properties exportedPropertyFileProps = new Properties();
    exportedPropertyFileProps.load( getClass().getResourceAsStream( "/exportedFromPME_en_US.properties" ) );

    // import the properties into the domain
    List<String> messages = util.analyzeImport( domain, exportedPropertyFileProps, "en_US" );
    if ( messages.isEmpty() ) {
      Assert.assertTrue( messages.isEmpty() );
    } else {
      for ( String message : messages ) {
        System.out.println( message );
      }
      Assert.fail( "The analysis of the export failed." );
    }
  }

}