/*
 * 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) 2009 - 2017 Hitachi Vantara..  All rights reserved.
 */
package org.pentaho.metadata.query.model.util;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeSet;

import org.pentaho.commons.connection.memory.MemoryMetaData;
import org.pentaho.metadata.messages.Messages;
import org.pentaho.metadata.model.LogicalColumn;
import org.pentaho.metadata.query.model.Selection;

/**
 * This class extends The Pentaho Connection API's MemoryMetaData with additional Query Selection Metadata.
 * 
 */
public class QueryModelMetaData extends MemoryMetaData {

  private List<? extends Selection> columns;

  public QueryModelMetaData( Object[][] columnHeaders, Object[][] rowHeaders, String columnNameFormatStr,
      String[] columnTypes, String[] columnNames, String[] rowHeaderNames, List<? extends Selection> columns ) {
    super( columnHeaders, rowHeaders, columnNameFormatStr, columnTypes, columnNames, rowHeaderNames );
    this.columns = columns;
  }

  public QueryModelMetaData( QueryModelMetaData metadata ) {
    super( metadata );
    this.columns = metadata.getColumns();
  }

  /**
   * 
   * In some database implementations, the "as" identifier has a finite length; for instance, Oracle cannot handle a
   * name longer than 30 characters. So, we map a short name here to the longer id, and replace the id later in the
   * resultset metadata.
   * 
   * @param columnsMap
   *          a map representing the truncated column names used in the query, mapped to the user-requested column id
   *          identifiers
   * @param columnHeaders
   *          column headers from the native metadata object
   * @param rowHeaders
   *          row headers from the native metadata object
   */
  public QueryModelMetaData( Map columnsMap, Object[][] columnHeaders, Object[][] rowHeaders,
      List<? extends Selection> selections ) {
    super( columnHeaders, rowHeaders );
    this.columns = selections;

    Object[][] newHeaders = columnHeaders;
    Object key;

    if ( columnsMap != null ) {
      Map<String, String> upperColumnMap = null;
      TreeSet<String> existingHeaders = new TreeSet<String>();
      newHeaders = new Object[columnHeaders.length][];
      String newHeader = null;
      for ( int i = 0; i < columnHeaders.length; i++ ) {
        newHeaders[i] = new Object[columnHeaders[i].length];
        for ( int j = 0; j < columnHeaders[i].length; j++ ) {
          key = columnHeaders[i][j];
          if ( key != null ) {
            newHeader = (String) columnsMap.get( key.toString().toUpperCase() );
            if ( newHeader == null ) {
              // Look up key by raw value (required by Hive until support is added for column aliases)
              newHeader = (String) columnsMap.get( key.toString() );
            }
            if ( newHeader == null ) {
              if ( upperColumnMap == null ) {
                upperColumnMap = upperCaseKeys( columnsMap );
              }
              newHeader = upperColumnMap.get( key.toString().toUpperCase() );
            }
            if ( newHeader == null ) {
              throw new RuntimeException( Messages.getErrorString(
                  "QueryModelMetaData.ERROR_0001_MetadataColumnNotFound", key.toString() ) ); //$NON-NLS-1$
            }
            newHeader = getUniqueHeader( newHeader, existingHeaders );
            existingHeaders.add( newHeader );
            newHeaders[i][j] = newHeader;
          }
        }
      }

      this.columnHeaders = newHeaders;
    }
  }

  private Map<String, String> upperCaseKeys( Map<?, ?> source ) {
    Map<String, String> result = new HashMap<String, String>( source.size() );
    for ( Entry<?, ?> entry : source.entrySet() ) {
      String key = null;
      if ( entry.getKey() != null ) {
        key = String.valueOf( entry.getKey() ).toUpperCase();
      }
      String value = null;
      if ( entry.getValue() != null ) {
        value = String.valueOf( entry.getValue() );
      }
      result.put( key, value );
    }
    return result;
  }

  private String getUniqueHeader( String header, TreeSet<String> existingHeaders ) {
    String newHeader = header;
    int count = 1;
    while ( existingHeaders.contains( newHeader ) ) {
      newHeader = header + "_" + count++; //$NON-NLS-1$
    }
    return newHeader;
  }

  public Object getAttribute( int rowNo, int columnNo, String attributeName ) {
    // TODO support OLAP
    if ( rowNo == 0 && columnNo < columns.size() ) {
      LogicalColumn column = columns.get( columnNo ).getLogicalColumn();
      return column.getProperty( attributeName );
    }
    return null;
  }

  public List<? extends Selection> getColumns() {
    return columns;
  }

}