/*
 * Copyright 2005 Red Hat, Inc. and/or its affiliates.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.drools.decisiontable.parser;

import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;

import org.drools.template.parser.DecisionTableParseException;

/**
 * Simple holder class identifying a condition, action or attribute column, also
 * including the rule name and a comment (called "description").
 * Its objects are stored in a map in the main listener class, to track what type of values
 * you can expect to see in the rows directly below the column header, identified
 * by an ActionType.Code.
 */
public class ActionType {

    public enum Code {
        CONDITION( "CONDITION", "C" ),
        ACTION( "ACTION", "A" ),
        NAME( "NAME", "N", 1 ),
        DESCRIPTION( "DESCRIPTION", "I" ),
        SALIENCE( "PRIORITY", "P", 1 ),
        DURATION( "DURATION", "D", 1 ),
        TIMER( "TIMER", "T", 1 ),
        ENABLED( "ENABLED", "B", 1 ),
        CALENDARS( "CALENDARS", "E", 1 ),
        NOLOOP( "NO-LOOP", "U", 1 ),
        LOCKONACTIVE( "LOCK-ON-ACTIVE", "L", 1 ),
        AUTOFOCUS( "AUTO-FOCUS", "F", 1 ),
        ACTIVATIONGROUP( "ACTIVATION-GROUP", "X", 1 ),
        AGENDAGROUP( "AGENDA-GROUP", "G", 1 ),
        RULEFLOWGROUP( "RULEFLOW-GROUP", "R", 1 ),
        DATEEFFECTIVE( "DATE-EFFECTIVE", "V", 1 ),
        DATEEXPIRES( "DATE-EXPIRES", "Z", 1 ),
        METADATA( "METADATA", "@" );

        private String colHeader;
        private String colShort;
        private int maxCount;

        /**
         * Constructor.
         * @param colHeader the column header
         * @param colShort a single letter, recognized as initial
         * @param maxCount maximum number of permitted columns
         */
        Code( String colHeader,
              String colShort,
              int maxCount ) {
            this.colHeader = colHeader;
            this.colShort = colShort;
            this.maxCount = maxCount;
        }

        Code( String colHeader,
              String colShort ) {
            this( colHeader, colShort, Integer.MAX_VALUE );
        }

        public String getColHeader() {
            return colHeader;
        }

        public String getColShort() {
            return colShort;
        }

        public int getMaxCount() {
            return maxCount;
        }
    }

    public static final EnumSet<Code> ATTRIBUTE_CODE_SET = EnumSet.range( Code.SALIENCE, Code.DATEEXPIRES );

    private static final Map<String, Code> tag2code = new HashMap<>();

    static {
        for ( Code code : EnumSet.allOf( Code.class ) ) {
            tag2code.put( code.colHeader, code );
            tag2code.put( code.colShort, code );
        }
    }

    private Code code;
    private SourceBuilder sourceBuilder = null;

    /**
     * Constructor.
     * @param actionTypeCode code identifying the column
     */
    ActionType( Code actionTypeCode ) {
        this.code = actionTypeCode;
    }

    public static EnumSet<Code> getAttributeCodeSet() {
        return ATTRIBUTE_CODE_SET;
    }

    public static Map<String, Code> getTag2code() {
        return tag2code;
    }

    /**
     * Retrieves the code.
     * @return an enum Code value
     */
    public Code getCode() {
        return this.code;
    }

    /**
     * This is only set for LHS or RHS building.
     */
    public void setSourceBuilder( SourceBuilder src ) {
        this.sourceBuilder = src;
    }

    public SourceBuilder getSourceBuilder() {
        return this.sourceBuilder;
    }

    /**
     * Create a new action type that matches this cell, and add it to the map,
     * keyed on that column.
     */
    public static void addNewActionType( final Map<Integer, ActionType> actionTypeMap,
                                         final String value,
                                         final int column,
                                         final int row ) {
        final String ucValue = value.toUpperCase();

        Code code = tag2code.get( ucValue );
        if ( code == null ) {
            code = tag2code.get( ucValue.substring( 0, 1 ) );
        }
        if ( code != null ) {

            int count = 0;
            for ( ActionType at : actionTypeMap.values() ) {
                if ( at.getCode() == code ) {
                    count++;
                }
            }
            if ( count >= code.getMaxCount() ) {
                throw new DecisionTableParseException( "Maximum number of " +
                                                               code.getColHeader() + "/" + code.getColShort() + " columns is " +
                                                               code.getMaxCount() + ", in cell " + RuleSheetParserUtil.rc2name( row, column ) );
            }
            actionTypeMap.put( Integer.valueOf( column ), new ActionType( code ) );
        } else {
            throw new DecisionTableParseException(
                    "Invalid column header: " + value + ", should be CONDITION, ACTION or attribute, " +
                            "in cell " + RuleSheetParserUtil.rc2name( row, column ) );
        }
    }

    /**
     * This is where a code snippet template is added.
     */
    public void addTemplate( int row,
                             int column,
                             String content ) {
        if ( this.sourceBuilder == null ) {
            throw new DecisionTableParseException(
                    "Unexpected content \"" + content + "\" in cell " +
                            RuleSheetParserUtil.rc2name( row, column ) + ", leave this cell blank" );
        }
        this.sourceBuilder.addTemplate( row, column, content );
    }

    public void addCellValue( int row,
                              int column,
                              String content,
                              boolean _escapeQuotesFlag ) {
        addCellValue( row, column, content, _escapeQuotesFlag, false );
    }

    /**
     * Values are added to populate the template.
     * The source builder contained needs to be "cleared" when the resultant snippet is extracted.
     */
    public void addCellValue( int row,
                              int column,
                              String content,
                              boolean _escapeQuotesFlag,
                              boolean trimCell ) {
        if ( _escapeQuotesFlag ) {
            //Michael Neale:
            // For single standard quotes we escape them - eg they may mean "inches" 
            // as in "I want a Stonehenge replica 19" tall"
            int idx = content.indexOf( "\"" );
            if ( idx > 0 && content.indexOf( "\"", idx ) > -1 ) {
                content = content.replace( "\"", "\\\"" );
            }
        }
        this.sourceBuilder.addCellValue( row, column, content, trimCell );
    }

}
