/* AbiSource Program Utilities
 * 
 * Copyright (C) 2005 Daniel d'Andrada T. de Carvalho
 * <daniel.carvalho@indt.org.br>
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
 * 02111-1307, USA.
 */
 
// Class definition include
#include "OD_ListLevelStyle.h"

// Internal includes
#include "OD_ListenerStateAction.h"
#include "OD_Style_Style.h"

// AbiWord includes
#include <pd_Document.h>
#include <fp_types.h>
#include <ut_string_class.h>
#include <ut_units.h>


/**
 * Constructor
 */
OD_ListLevelStyle::OD_ListLevelStyle(const char* pStateName,
									 OD_ElementStack& rElementStack) :
                        OD_ListenerState(pStateName, rElementStack),
                        m_pTextStyle(NULL)
{
}


/**
 * 
 */
void OD_ListLevelStyle::startElement (const XML_Char* pName,
                                      const XML_Char** ppAtts,
                                      OD_ListenerStateAction& rAction) {
    const XML_Char* pVal = NULL;

    if (!UT_strcmp("text:list-level-style-bullet", pName) ||
        !UT_strcmp("text:list-level-style-number", pName) ||
        !UT_strcmp("text:list-level-style-image", pName)) {

        UT_uint32 result;
            
        pVal = UT_getAttribute ("text:level", ppAtts);
        UT_ASSERT(pVal);
        
        result = sscanf(pVal, "%u", &m_levelNumber);
        UT_ASSERT(result==1);
        m_level = pVal;
        
        pVal = UT_getAttribute ("text:style-name", ppAtts);
        if (pVal) {
            m_textStyleName = pVal;
        }
        
    } else if (!UT_strcmp("style:list-level-properties", pName)) {

        pVal = UT_getAttribute ("text:space-before", ppAtts);
        if (pVal) {
            m_spaceBefore = pVal;
        } else {
            m_spaceBefore = "0cm";
        }
        
        pVal = UT_getAttribute ("text:min-label-width", ppAtts);
        if (pVal) {
            m_minLabelWidth = pVal;
        } else {
            m_minLabelWidth = "0cm";
        }
        
        pVal = UT_getAttribute ("text:min-label-distance", ppAtts);
        if (pVal) {
            m_minLabelDistance = pVal;
        }
    }
}


/**
 * 
 */
void OD_ListLevelStyle::endElement (const XML_Char* pName,
                                    OD_ListenerStateAction& rAction) {
                                        
    if (!UT_strcmp("text:list-level-style-bullet", pName) ||
        !UT_strcmp("text:list-level-style-number", pName) ||
        !UT_strcmp("text:list-level-style-image", pName)) {
            
        // We're done.
        rAction.popState();
    }
}


/**
 * 
 */
void OD_ListLevelStyle::setAbiListID(UT_uint32 abiListID) {
    XML_Char buffer[100];
    
    sprintf(buffer, "%u", abiListID);
    m_abiListID.assign(buffer);
}


/**
 * Defines a <l> tag on the AbiWord document corresponding to this
 * list level style.
 */
void OD_ListLevelStyle::defineAbiList(PD_Document* pDocument) {
    const XML_Char* ppAttr[13];
    
    ppAttr[0] = "id";
    ppAttr[1] = m_abiListID.utf8_str();
    ppAttr[2] = "parentid";
    ppAttr[3] = m_abiListParentID.utf8_str();
    ppAttr[4] = "type";
    ppAttr[5] = m_abiListType.utf8_str();
    ppAttr[6] = "start-value";
    ppAttr[7] = m_abiListStartValue.utf8_str();
    ppAttr[8] = "list-delim";
    ppAttr[9] = m_abiListListDelim.utf8_str();
    ppAttr[10] = "list-decimal";
    ppAttr[11] = m_abiListListDecimal.utf8_str();
    ppAttr[12] = 0;
    
    pDocument->appendList(ppAttr);
}


/**
 * 
 */
void OD_ListLevelStyle::buildAbiPropsString() {
    m_abiProperties.clear();
}


/**
 * The AbiWord properties of the list depends on some properties already
 * defined by the AbiWord paragraph style.
 * 
 * @param rProps Will have the properties string appended.
 * @param pStyle Pointer to the paragraph style used on this list paragraph.
 */    
void OD_ListLevelStyle::getAbiProperties(UT_UTF8String& rProps,
                                         const OD_Style_Style* pStyle) const {

    // Adds the fixed portion of the properties.
    if (!m_abiProperties.empty()) {
        if (!rProps.empty()) {
            rProps += "; ";
        }
        rProps += m_abiProperties;
    }
    
    
    // From the OpenDocument OASIS standard, v1.0:
    //
    // "The text:space-before attribute specifies the space to include before
    // the number for all paragraphs at this level. If a paragraph has a left
    // margin that is greater than 0, the actual position of the list label box
    // is the left margin width plus the start indent value."
    //
    // AbiWord's margin-left = OpenDocument paragraph property fo:margin-left +
    //                         OpenDocument text:space-before +
    //                         OpenDocument text:min-label-witdh
    //
    // AbiWord's text-indent = - (minus) OpenDocument text:min-label-width
    //

    if (pStyle != NULL) {
        if (!UT_strcmp(pStyle->getFamily()->utf8_str(), "paragraph")) {
            
            double spaceBefore_cm;
            double minLabelWidth_cm;
            const UT_UTF8String* pMarginLeft = pStyle->getMarginLeft();
            double styMarginLeft_cm;
            XML_Char buffer[100];
            
            spaceBefore_cm = UT_convertToDimension(m_spaceBefore.utf8_str(),
                                                   DIM_CM);
                                                   
            minLabelWidth_cm = UT_convertToDimension(m_minLabelWidth.utf8_str(),
                                                     DIM_CM);
                                                     
            styMarginLeft_cm = UT_convertToDimension(pMarginLeft->utf8_str(),
                                                     DIM_CM);
            
            sprintf(buffer, "%fcm", styMarginLeft_cm + spaceBefore_cm + 
                                    minLabelWidth_cm);
                                    
            if (!rProps.empty()) {
                rProps += "; ";
            }
            rProps += "margin-left:";
            rProps.append(buffer);
            
            sprintf(buffer, "%fcm", -minLabelWidth_cm);

            rProps += "; text-indent:";
            rProps.append(buffer);
        }
    }
    
}


/******************************************************************************/


/**
 * 
 */
OD_Bullet_ListLevelStyle::OD_Bullet_ListLevelStyle(OD_ElementStack& rElementStack)
	: OD_ListLevelStyle("Bullet_ListLevelStyle", rElementStack)
{
    XML_Char buffer[100];
    
    sprintf(buffer, "%d", BULLETED_LIST);
    m_abiListType.assign(buffer);
    
    // Dummy values
    m_abiListStartValue.assign("0");
    m_abiListListDelim.assign("%L");
    m_abiListListDecimal.assign("NULL");
}


/**
 * 
 */
void OD_Bullet_ListLevelStyle::buildAbiPropsString() {
    
    OD_ListLevelStyle::buildAbiPropsString();
    
    if (!m_abiProperties.empty()) {
        m_abiProperties += "; ";
    }
    
    // Some fixed values.
    m_abiProperties += "field-font:Symbol";
}


/******************************************************************************/


/**
 * Constructor
 */
OD_Numbered_ListLevelStyle::OD_Numbered_ListLevelStyle(OD_ElementStack& rElementStack)
    : OD_ListLevelStyle("Numbered_ListLevelStyle", rElementStack) {
        
    // It seens that OpenDocument aways uses a dot "." as level delimiter.
    m_abiListListDecimal = ".";
}


/**
 * 
 */
void OD_Numbered_ListLevelStyle::startElement (const XML_Char* pName,
                                               const XML_Char** ppAtts,
                                               OD_ListenerStateAction& rAction) {

    const XML_Char* pVal;

    // Let the parent class do the processing common to all list types.
    OD_ListLevelStyle::startElement (pName, ppAtts, rAction);
                                                
    if (!UT_strcmp("text:list-level-style-number", pName)) {
        UT_UTF8String prefix, suffix;
        
        pVal = UT_getAttribute ("style:num-format", ppAtts);
        UT_ASSERT(pVal);
        _setAbiListType(pVal);

        if (!UT_strcmp(pVal, "")) {
            // We have an empty number format.
            
            // Empty list label or "invisible" list.
            m_abiListListDelim = "";
            
        } else {
            // We have a number format defined.
            
            pVal = UT_getAttribute ("style:num-prefix", ppAtts);
            if(pVal) {
                prefix = pVal;
            }
            
            pVal = UT_getAttribute ("style:num-suffix", ppAtts);
            if(pVal) {
                suffix = pVal;
            }
            
            m_abiListListDelim  = prefix;
            m_abiListListDelim += "%L";
            m_abiListListDelim += suffix;
        }
        
        pVal = UT_getAttribute ("text:start-value", ppAtts);
        if(pVal) {
            m_abiListStartValue = pVal;
        } else {
            // AbiWord's default value is 0, but on OpenDocument it's 1.
            m_abiListStartValue = "1";
        }
    }
}


/**
 * 
 */
void OD_Numbered_ListLevelStyle::buildAbiPropsString() {
    
    OD_ListLevelStyle::buildAbiPropsString();
    
    if (!m_abiProperties.empty()) {
        m_abiProperties += "; ";
    }
    
    m_abiProperties += "field-font: ";
    if (m_pTextStyle) {
        m_abiProperties += *(m_pTextStyle->getFontName());
    } else {
        m_abiProperties += "NULL";
    }
}


/**
 * Maps the value of the OpenDocument attribute style:num-format to the
 * correspondent AbiWord "type" attribute of the list (<l>) element tag.
 * 
 * @param pStyleNumFormat The value of the style:num-format attribute.
 */
void OD_Numbered_ListLevelStyle::_setAbiListType(const XML_Char* pStyleNumFormat) {

    XML_Char buffer[100];
    
    if (!pStyleNumFormat) {
        // Use an arbitrary list type.
        sprintf(buffer, "%d", NUMBERED_LIST);
        
    } else if (!UT_strcmp(pStyleNumFormat, "1")) {
        sprintf(buffer, "%d", NUMBERED_LIST);
        
    } else if (!UT_strcmp(pStyleNumFormat, "a")) {
        sprintf(buffer, "%d", LOWERCASE_LIST);
        
    } else if (!UT_strcmp(pStyleNumFormat, "A")) {
        sprintf(buffer, "%d", UPPERCASE_LIST);
        
    } else if (!UT_strcmp(pStyleNumFormat, "i")) {
        sprintf(buffer, "%d", LOWERROMAN_LIST);
        
    } else if (!UT_strcmp(pStyleNumFormat, "I")) {
        sprintf(buffer, "%d", UPPERROMAN_LIST);
        
    } else {
        // Use an arbitrary list type.
        sprintf(buffer, "%d", NUMBERED_LIST);
    }
    
    m_abiListType.assign(buffer);
}
