/*!
  \file    DBMSrvCmd_Command.cpp
  \author  MarcW
  \ingroup common classes for the DBMServer
  \brief   definition of base class for DBMServer commands

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2002-2005 SAP AG

    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.
    ========== licence end


\endif
*/

#include <limits.h>

#include "hcn10.h"
#include "hcn20.h"
#include "hcn36.h"
#include "hcn90.h"

#include "DBM/Srv/DBMSrv_Command.hpp"
#include "SAPDB/DBM/Srv/ExplainHelp/DBMSrvExpHlp_CommandPart.hpp"
#include "SAPDB/DBM/Srv/ExplainHelp/DBMSrvExpHlp_ParameterPart.hpp"
#include "SAPDB/DBM/Srv/ExplainHelp/DBMSrvExpHlp_PreconditionsPart.hpp"
#include "SAPDB/DBM/Srv/ExplainHelp/DBMSrvExpHlp_ReplyPart.hpp"
#include "SAPDB/DBM/Srv/ExplainHelp/DBMSrvExpHlp_ReplyValuePart.hpp"
#include "SAPDB/DBM/Srv/ExplainHelp/DBMSrvExpHlp_SyntaxPart.hpp"
#include "SAPDB/DBM/Srv/ExplainHelp/DBMSrvExpHlp_ValuePart.hpp"

#include "DBM/Srv/Commands/DBMSrvCmd_DbmVersion.hpp"
#include "DBM/Srv/Commands/DBMSrvCmd_Help.hpp"
#include "DBM/Srv/Commands/DBMSrvCmd_HssCopyFile.hpp"
#include "DBM/Srv/Commands/DBMSrvCmd_InstInfo.hpp"
#include "DBM/Srv/Commands/SQL/DBMSrvCmdSQL_SqlUpdatestatPerSystemtable.hpp"

DBMSrv_Command* DBMSrv_Command::getCommand(
        CommandKey aCommandKey,
        DBMSrv_Reply& theReply) {

    DBMSrv_Command * rc=0;
    bool keyFound=true;

    switch( aCommandKey ) {
        case KeyDbmVersion:               rc=new DBMSrvCmd_DbmVersion(); break;
        case KeyHelp:                     rc=new DBMSrvCmd_Help(); break;
        case KeyHssCopyFile:              rc=new DBMSrvCmd_HssCopyFile(); break;
        case KeyInstInfo:                 rc=new DBMSrvCmd_InstInfo(); break;
        case KeySqlUpdatestatPerSystemtable:
                                          rc=new DBMSrvCmdSQL_SqlUpdatestatPerSystemtable(); break;
        default:                          keyFound=false; break;
    }

    if(0==rc)
    {
        if(keyFound)                                //key found, but new returned 0
            theReply.startWithError(ERR_MEM_CN00);
        else                                        //known key not found in the switch statement
            theReply.startWithError(ERR_NOTIMPL_CN00);
    }

    return rc;
}

// public member function DBMSrv_Command::getRundirectory
const char* DBMSrv_Command::getRundirectory(bool ReadAgain) {
    if( m_RundirectoryNotCalculated || ReadAgain) {
      m_RundirectoryNotCalculated = !getParameter(PAN_RUNDIR, m_Rundirectory);
    } // end if
    return m_Rundirectory;
} // end DBMSrv_Command::getRundirectory

// public member function DBMSrv_Command::getUnicode
const char* DBMSrv_Command::getUnicode(bool ReadAgain) {
    if( m_UnicodeNotCalculated || ReadAgain) {
      m_UnicodeNotCalculated = !getParameter(PAN_UNICODE_CATALOG, m_Unicode);
    } // end if
    return m_Unicode;
} // end DBMSrv_Command::getUnicode

// public member function DBMSrv_Command::InstanceType
const char* DBMSrv_Command::getInstanceType(bool ReadAgain) {
    if( m_InstanceTypeNotCalculated || ReadAgain) {
      m_InstanceTypeNotCalculated = !getParameter(PAN_INSTANCE_TYPE, m_InstanceType);
    } // end if
    return m_InstanceType;
} // end DBMSrv_Command::getInstanceType

// private member function DBMSrv_Command::getParameter
bool DBMSrv_Command::getParameter
    ( const char           * szName,
      tcn002_XpValueString & aValue )
{
  if( strlen((const char*)cn00DBMServerData::vcontrol()->dbname) > 0 ) {
    return (cn20XParamGetValue(cn00DBMServerData::vcontrol()->dbname, szName, aValue) == OK_CN00);
  } // end if

  return false;
} // end DBMSrv_Command::getUnicode

void DBMSrv_Command::formatExplainText(CommandT* command, DBMSrv_Reply& theReply, const char* longHelp) {
    bool ParameterFound=false;
    bool ExplainCompleteCommand=true;
    bool ShowLongNames=false;
    size_t LineSize=80;

    const char * Command=0;
    const char * CommandEnd=0;
    const char * Option=0;
    const char * OptionEnd=0;
    const char * DummyParameter=0;
    const char * DummyParameterEnd=0;

    theReply.startWithOK();

    // analyze parameters
    cn90GetStartAndEndOfToken(command->args, Command, CommandEnd, 1);
    cn90GetStartAndEndOfToken(command->args, Option, OptionEnd, 2);
    cn90GetStartAndEndOfToken(command->args, DummyParameter, DummyParameterEnd, 3);

    char cmdName[80];
    size_t len(CommandEnd-Command>79?79:CommandEnd-Command);
    strncpy(cmdName, Command, len);
    cmdName[len] = '\0';

    if(DummyParameter!=DummyParameterEnd)
    {
        if(0==cn36_StrStrNUprCmp("-NOLINEWRAP", DummyParameter, DummyParameterEnd-DummyParameter)) // we have a third parameter and it is a "-nolinewrap"
            LineSize=INT_MAX;

        if(0==cn36_StrStrNUprCmp("-LONGINFO", DummyParameter, DummyParameterEnd-DummyParameter))
        {
            LineSize=INT_MAX;
            ShowLongNames=true;
        }     
    }

    if(Option!=OptionEnd)
    {
        if(Option[0]=='"' && 2<=OptionEnd-Option && '"'==*(OptionEnd-1)) {
            // if option is in "..."
            Option++;
            OptionEnd--;
        }
        ExplainCompleteCommand=false;
    }

    if(longHelp == NULL)
        theReply.appendStringsAndLine("No explanation available for command ", cmdName, ".");
    else
    {
        DBMSrvExpHlp_CommandPart       CommandInfo(longHelp);       //search for first "@command" part
        DBMSrvExpHlp_PreconditionsPart PreconditionsInfo(longHelp); //search for the first "@preconditions" part
        DBMSrvExpHlp_SyntaxPart        SyntaxInfo(longHelp);        //search for first "@syntax" part
        DBMSrvExpHlp_ParameterPart     ParameterInfo(longHelp);     //search for first "@param" part
        DBMSrvExpHlp_ReplyPart         ReplyPart(longHelp);         //search for first "@reply" part

        if(CommandInfo.wasFound())
        {
            if(ExplainCompleteCommand) // the complete explanation of a command was requested
            {
                //print command info
                if(CommandInfo.hasExplanation())
                    theReply.appendIndentedWrappedLineN(CommandInfo.giveExplanationStart(), CommandInfo.giveExplanationLength(), 0, LineSize, "<NL>", 0);
                else
                    theReply.appendStringsAndLine("No explanation available for command ", cmdName, ".");

                //print preconditions
                if(PreconditionsInfo.wasFound())
                {
                    theReply.appendLines("", "Preconditions:");
                    theReply.appendIndentedWrappedLineN(PreconditionsInfo.giveExplanationStart(), PreconditionsInfo.giveExplanationLength(), 4, LineSize, 0);
                }

                //print syntax
                theReply.appendLines("", "Syntax:");
                theReply.appendStrings("    ", cmdName);

                if(SyntaxInfo.hasExplanation())
                {
                    size_t LengthBeforeWrappedPart=4+strlen(cmdName)+1;

                    theReply.appendString(" ");
                    theReply.appendIndentedWrappedLineN(SyntaxInfo.giveExplanationStart(), SyntaxInfo.giveExplanationLength(), LengthBeforeWrappedPart, LineSize, LengthBeforeWrappedPart);
                }
                else //if the command has no syntax info we assume it has no parameters
                    theReply.appendLine();
            }

            //explain parameters
            if(ParameterInfo.wasFound())
            {
                bool   IsFirstOption=true;
                size_t MaxParameterLength=ParameterInfo.LongestIDOfSameType();

                if(ExplainCompleteCommand)
                    theReply.appendLines("", "Options:");

                while(ParameterInfo.wasFound() && !ParameterFound)
                {
                    //explain the parameter
                    if(ExplainCompleteCommand)
                    {
                        if(IsFirstOption)
                            IsFirstOption=false;
                        else
                            theReply.appendLine();

                        theReply.appendStringAndLineN("    ", ParameterInfo.giveIDStart(), ParameterInfo.giveIDLength());
                    }
                    else
                        if(ParameterInfo.giveIDLength()==OptionEnd-Option &&
                            0==strnicmp(Option, ParameterInfo.giveIDStart(), ParameterInfo.giveIDLength()))
                        {
                            ParameterFound=true;
                        }

                        if(ExplainCompleteCommand || ParameterFound)
                        {
                            size_t ParameterIndent=ParameterFound?0:8;

                            theReply.appendIndentedWrappedLineN(ParameterInfo.giveExplanationStart(),
                                ParameterInfo.giveExplanationLength(),
                                ParameterIndent,
                                LineSize,
                                0);

                            //explain special values for currently explained parameter
                            DBMSrvExpHlp_ValuePart ValueInfo(ParameterInfo.giveExplanationEnd());

                            if(ValueInfo.wasFound())
                            {
                                size_t MaxValueLength=ValueInfo.LongestIDOfSameType();
                                size_t MaxLongNameLength=ValueInfo.LongestLongNameOfSameType();
                                size_t Ident=ShowLongNames?0:ParameterIndent+4;

                                while(ValueInfo.wasFound())
                                {
                                    theReply.appendCharacterRepeatedly(' ', Ident);
                                    theReply.appendStringNWithMinWidth(
                                        ValueInfo.giveIDStart(),
                                        ValueInfo.giveIDLength(),
                                        ShowLongNames?MaxValueLength:MaxValueLength+1);

                                    if(ShowLongNames)
                                    {
                                        theReply.appendString("|");
                                        theReply.appendStringNWithMinWidth(ValueInfo.giveLongNameStart(), ValueInfo.giveLongNameLength(), MaxLongNameLength);
                                        theReply.appendString("|");
                                    }

                                    theReply.appendIndentedWrappedLineN(ValueInfo.giveExplanationStart(), ValueInfo.giveExplanationLength(), Ident+MaxValueLength+1, LineSize, Ident+MaxValueLength+1);

                                    ValueInfo.setToNextParameter();
                                }
                            }
                        }

                        //switch to the next parameter
                        ParameterInfo.setToNextParameter();
                }

            }

            if(!ExplainCompleteCommand && !ParameterFound) //one parameter should be explained, but no explanation was found in longHelp
                theReply.appendStringsStringNAndLine("Command ", cmdName, " has no option ", Option, OptionEnd-Option, ".");

            //explain reply format
            if(ExplainCompleteCommand && ReplyPart.wasFound() && ReplyPart.hasExplanation())
            {
                theReply.appendLines("", "Reply:");
                theReply.appendIndentedWrappedLineN(ReplyPart.giveExplanationStart(), ReplyPart.giveExplanationLength(), 4, LineSize, "<NL>", 0);

                DBMSrvExpHlp_ReplyValuePart ReplyValue(ReplyPart.giveExplanationEnd());

                if(ReplyValue.wasFound())
                {
                    theReply.appendLine();
                    theReply.appendStringAndLine("    ", "Meaning of the individual fields of the reply:");

                    size_t MaxValueLength=ReplyValue.LongestIDOfSameType();
                    bool IsFirstReplyValue=true;

                    while(ReplyValue.wasFound())
                    {
                        if(IsFirstReplyValue)
                            IsFirstReplyValue=false;
                        else
                            theReply.appendLine();

                        theReply.appendStringAndLineN("        ", ReplyValue.giveIDStart(), ReplyValue.giveIDLength());
                        theReply.appendIndentedWrappedLineN(ReplyValue.giveExplanationStart(), ReplyValue.giveExplanationLength(), 12, LineSize, 0);

                        ReplyValue.setToNextParameter();
                    }
                }
            }
        }
        else
            theReply.appendLine(longHelp); //there is a longHelp, but it does not have even a "@command" -> simply print the longHelp string
    }
}

void DBMSrv_Command::formatSyntaxText( DBMSrv_Reply& Reply, const char* longHelp ) {

    size_t LineSize(80);
    size_t IndentSize(25);

    if( longHelp != NULL ) {
        DBMSrvExpHlp_SyntaxPart SyntaxInfo(longHelp);
        if(SyntaxInfo.wasFound())
        {
            size_t LengthBeforeWrappedPart(SyntaxInfo.giveIDLength());

            if(LengthBeforeWrappedPart<IndentSize)
                LengthBeforeWrappedPart=IndentSize;

            Reply.appendStringNWithMinWidth(SyntaxInfo.giveIDStart(), SyntaxInfo.giveIDLength(), IndentSize-1);
            Reply.appendString(" ");
            Reply.appendIndentedWrappedLineN(SyntaxInfo.giveExplanationStart(), SyntaxInfo.giveExplanationLength(), LengthBeforeWrappedPart, LineSize, LengthBeforeWrappedPart);
        }
    }
}

bool DBMSrv_Command::getExplainText(CommandT* command, DBMSrv_Reply& theReply)
{
    if(0!=m_longHelp)
        formatExplainText(command, theReply, m_longHelp);

    return (0!=m_longHelp);
}

bool DBMSrv_Command::getSyntaxText(DBMSrv_Reply& theReply)
{
    if(0!=m_longHelp)
        formatSyntaxText(theReply, m_longHelp);

    return (0!=m_longHelp);
}

bool DBMSrv_Command::getUsageText(DBMSrv_Reply& theReply) {
    theReply.appendLine("usage:");
    return getSyntaxText( theReply );
}
