/* 
 * $Id: ImportTextSourcecode.cpp,v 1.17 2006/03/05 05:00:09 ozawa Exp $
 *
 * Copyright 2005- ONGS Inc. All rights reserved.
 * 
 * author: Masanori OZAWA (ozawa@ongs.co.jp)
 * version: $Revision: 1.17 $
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY ONGS INC ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL ONGS INC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the ONGS Inc.
 * 
 */

#include "xdtp.h"
#include "modules/imports/ImportTextSourcecode.h"
#include "Utilities.h"
#include "URLTool.h"
#include "XMLTool.h"

#define TYPE_SOURCECODE "text/sourcecode"

using namespace XDTP;

ImportTextSourcecode::ImportTextSourcecode()
{
}

ImportTextSourcecode::~ImportTextSourcecode()
{
}

/**
 * XSL変換後のXML文章を保存する直前に呼び出されます。
 * 
 * @param aDocument XML文章
 */
void ImportTextSourcecode::treatPostDocument(xmlDocPtr aDocument)
{
    GLSDImportModuleAdapter::treatPostDocument(aDocument);

    int count;
    XMLTool tool;
    xmlNodePtr root = xmlDocGetRootElement(aDocument);
    xmlNodeSetPtr nodes;
    xmlXPathObjectPtr obj = tool.getNodeList(root, "//import[@type=\"text/sourcecode\"]");

    m_needTIP = false;

    if (obj) {
        nodes = obj->nodesetval;
        
        for (count = 0; count < nodes->nodeNr; count++) {
            if (!transform(aDocument, nodes->nodeTab[count])) {
                THROW("ImportTextSourcecode: transform failed.");
            }
        }
        
        xmlXPathFreeObject(obj);
    }
}

/**
 * XML文章を保存した直後に呼び出されます。
 * 
 * @param aFile 保存したファイル名
 */
void ImportTextSourcecode::treatPostFile(const Glib::ustring& aFile)
{
    GLSDImportModuleAdapter::treatPostFile(aFile);

    if (m_needTIP) {
        if (!transformImportPoint(aFile, TYPE_SOURCECODE)) {
            THROW("ImportTextSourcecode: Unable to transform the import point.");
        }
    }
}

bool ImportTextSourcecode::transform(xmlDocPtr aDocument, xmlNodePtr aNode)
{
    Glib::ustring ref;
    Glib::ustring caption;
    Glib::ustring encoding;

    if (!getImportAttributes(aNode, ref, caption, encoding)) {
        return false;
    }
    
    if ("html" == m_OutputType) {
        return sourcecodeTo_nHTML(aDocument, aNode, ref, caption, encoding);
    }
    else if ("xhtml" == m_OutputType) {
        return sourcecodeTo_nHTML(aDocument, aNode, ref, caption, encoding);
    }
    else if ("text" == m_OutputType) {
        return preserveImport(aDocument, aNode, ref, caption, encoding);
    }
    // otherwise, do not operation.
    
    return true;
}

bool ImportTextSourcecode::sourcecodeTo_nHTML
    (xmlDocPtr aDocument, xmlNodePtr aNode,
     const Glib::ustring& aRef,
     const Glib::ustring& aCaption,
     const Glib::ustring& anEncoding)
{
    // スタイルシートを取得
    xmlNodePtr comment = getStyleSheet(aDocument, "text/css");

    if (comment) {
        // スタイルシートにイメージのスタイルの指定があるか確認
        Glib::ustring content = (comment->content ? (const char*)comment->content : "");
        Glib::ustring::size_type pos = content.find("div.sourcecode");
        
        if (Glib::ustring::npos == pos) {
            // スタイルシートにイメージのスタイルを追加
            content += LINE_SEP;
            content += "   div.sourcecode {" LINE_SEP;
            content += "     margin: 1.5em 0 0.5em 0;" LINE_SEP;
            content += "     text-align: center;" LINE_SEP;
            content += "   }" LINE_SEP;
            content += "   blockquote.sourcecode {" LINE_SEP;
            content += "     margin: 1.0mm 0 2.0mm 0;" LINE_SEP;
            content += "     padding: 0.5em 1.0em 0.5em 1.0em;" LINE_SEP;
            content += "     border: solid thin silver;" LINE_SEP;
            content += "     overflow: clip;" LINE_SEP;
            content += "   }" LINE_SEP;
            xmlNodeSetContent(comment, (const xmlChar*)content.c_str());
        }
    }
    
    m_needTIP = true;
    
    xmlNodePtr div = xmlNewNode(NULL, (const xmlChar*)"div");
    if (!div) THROW("Out of memory!");
    xmlSetProp(div, (const xmlChar*)"style",
                (const xmlChar*)"text-indent: 0em; line-height: 1.2em;");
    xmlAddChild(div, createNewImportPointNode(TYPE_SOURCECODE, aRef, aCaption, anEncoding));

    xmlNodePtr block = xmlNewNode(NULL, (const xmlChar*)"blockquote");
    if (!block) {
        xmlFreeNode(div);
        THROW("Out of memory!");
    }
    xmlSetProp(block, (const xmlChar*)"cite", (const xmlChar*)aRef.c_str());
    xmlSetProp(block, (const xmlChar*)"class", (const xmlChar*)"sourcecode");
    xmlAddChild(block, div);

    xmlNodePtr title = xmlNewNode(NULL, (const xmlChar*)"div");
    if (!title) {
        xmlFreeNode(block);
        THROW("Out of memory!");
    }
    xmlSetProp(title, (const xmlChar*)"class", (const xmlChar*)"sourcecode");
    xmlAddChild(title, xmlNewText((const xmlChar*)aCaption.c_str()));
    
    xmlFreeNode(xmlReplaceNode(aNode, block));
    
    xmlAddPrevSibling(block, title);
    
    return true;
}

/**
 * ファイルからインポート処理対象の部分を抽出し、置換処理を
 * 行う際に呼び出されます。
 * 
 * @param aRef インポート先のファイル
 * @param aCaption キャプション
 * @param anEncoding インポート先のファイルのエンコード
 * @param aNewData 置換すべき文字列を応答します。
 * @return 処理結果を応答します。
 */
TIP_STATUS ImportTextSourcecode::transformImportPointCallback
    (const Glib::ustring& aRef, const Glib::ustring& aCaption,
     const Glib::ustring& anEncoding, Glib::ustring& aNewData)
{
    // ファイルを読み込み、テキストノードを構築する
    bool bFailed = false;
    std::string tmpfile;

    try {
        Glib::RefPtr<Glib::IOChannel> ioc = getIOChannel(aRef, tmpfile);
        setEncoding(ioc, anEncoding);
        
        Glib::ustring line;
        Glib::ustring space = "        "; // space 8
        Glib::ustring::size_type pos;
        
        while (ioc->read_line(line) == Glib::IO_STATUS_NORMAL) {
            line = Utilities::strTrimCRLF(line);
            if ("html" == m_OutputType) {
                // タブをスペース展開する
                for (pos = line.find('\t');
                    Glib::ustring::npos != pos;
                    pos = line.find('\t'))
                {
                    line = line.substr(0, pos)
                            + space.substr(pos % 8)
                            + line.substr(pos + 1);
                }

                // エスケープ処理
                line = escapeString(line);
            }

            aNewData += line + "<br/>" LINE_SEP;
        }
        ioc->close();
    }
    catch (Exception exception) {
        exception.print(stderr);
        bFailed = true;
    }
    catch (Glib::Error error) {
        std::string msg = Glib::locale_from_utf8(error.what());
        fprintf(stderr, "%s: %s (%s)" LINE_SEP, APP_NAME, msg.c_str(), aRef.c_str());
        bFailed = true;
    }
    catch (std::exception exception) {
        fprintf(stderr, "%s: %s (%s)" LINE_SEP, APP_NAME, exception.what(), aRef.c_str());
        bFailed = true;
    }
    catch (...) {
        fprintf(stderr, "%s: ImportTextSourcecode: unknown error occured. (%s)" LINE_SEP,
            APP_NAME, aRef.c_str());
        bFailed = true;
    }

    if (0 < tmpfile.length()) {
        remove(tmpfile.c_str());
    }
    
    return (bFailed ? TIP_FAILED : TIP_SUCCESS);
}


syntax highlighted by Code2HTML, v. 0.9.1