/*!
  \file    DBMSrvCmd_Explain.cpp
  \author  TiloH
  \ingroup DBMServer commands
  \brief   implementation of a class for the DBMServer command explain

\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
*/

//-----------------------------------------------------------------------------
// includes
//-----------------------------------------------------------------------------

#include <limits.h>

#include "SAPDB/DBM/Srv/DBMSrv_Reply.hpp"
#include "SAPDB/DBM/Srv/Commands/DBMSrvCmd_Explain.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 "hcn10.h"
#include "hcn36.h"
#include "hcn90.h"


//-----------------------------------------------------------------------------
// members of class DBMSrvCmd_Explain
//-----------------------------------------------------------------------------

const char * DBMSrvCmd_Explain::longHelp=
    "@command explain A function for explaining DBM commands."
    "@preconditions There are none."
    "@syntax explain <command> [<option> [-nolinewrap|-longinfo]]"
    "@param <command>   The DBMServer command that will be explained."
    "@param <option>    Restricts the output to the explanation of the specified command option."
    "@param -nolinewrap Suppresses line wraps normally inserted for better readability."
    "@param -longinfo   Suppresses line wraps normally inserted for better readability, "
                       "shows the descriptive names of the allowed values for option <option> and "
                       "separates allowed values, long names and explanation of the value by |.";

tcn00_Error DBMSrvCmd_Explain::runCommand(VControlDataT * vcontrol,
                                          CommandT      * command,
                                          char          * replyData,
                                          int           * replyLen,
                                          int             replyLenMax)
{
    bool CommandFound=false;
    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;

    const cn10DBMServerCommand * CommandInArray=DBMServerCommandsArray_cn10;

    DBMSrv_Reply Reply(replyData, replyLen, replyLenMax);

    Reply.startWithOK();

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

    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))
        {
            Option++;
            OptionEnd--;
        }

        ExplainCompleteCommand=false;
    }

    //search the command in the array of command information
    while(0!=CommandInArray->name && !CommandFound)
    {
        if(Command!=CommandEnd && 0==cn36_StrStrNUprCmp(CommandInArray->name, Command, CommandEnd-Command)) //<command> parameter is set and matches namesC[nIndex].name
            CommandFound=true;
        else
            ++CommandInArray;
    }

    //if the command was found, explain it
    if(CommandFound)
    {
        if(0==CommandInArray->longHelp)
            Reply.appendStringsAndLine("No explanation is available for command ", CommandInArray->name, ".");
        else
        {
            DBMSrvExpHlp_CommandPart       CommandInfo(CommandInArray->longHelp);       //search for first "@command" part
            DBMSrvExpHlp_PreconditionsPart PreconditionsInfo(CommandInArray->longHelp); //search for the first "@preconditions" part
            DBMSrvExpHlp_SyntaxPart        SyntaxInfo(CommandInArray->longHelp);        //search for first "@syntax" part
            DBMSrvExpHlp_ParameterPart     ParameterInfo(CommandInArray->longHelp);     //search for first "@param" part
            DBMSrvExpHlp_ReplyPart         ReplyPart(CommandInArray->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())
                        Reply.appendIndentedWrappedLineN(CommandInfo.giveExplanationStart(), CommandInfo.giveExplanationLength(), 0, LineSize, "<NL>", 0);
                    else
                        Reply.appendStringsAndLine("No explanation is available for command ", CommandInArray->name, ".");

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

                    //print syntax
                    Reply.appendLines("", "Syntax:");
                    Reply.appendStrings("    ", CommandInArray->name);

                    if(SyntaxInfo.hasExplanation())
                    {
                        size_t LengthBeforeWrappedPart=4+strlen(CommandInArray->name)+1;

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

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

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

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

                            Reply.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;

                            Reply.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())
                                {
                                    Reply.appendCharacterRepeatedly(' ', Ident);
                                    Reply.appendStringNWithMinWidth(
                                        ValueInfo.giveIDStart(),
                                        ValueInfo.giveIDLength(),
                                        ShowLongNames?MaxValueLength:MaxValueLength+1);

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

                                    Reply.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
                    Reply.appendStringsStringNAndLine("I'm sorry, the command ", CommandInArray->name, " has no option ", Option, Option-OptionEnd, ".");

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

                    DBMSrvExpHlp_ReplyValuePart ReplyValue(ReplyPart.giveExplanationEnd());

                    if(ReplyValue.wasFound())
                    {
                        Reply.appendLine();
                        Reply.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
                                Reply.appendLine();

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

                            ReplyValue.setToNextParameter();
                        }
                    }
                }
            }
            else
                Reply.appendLine(CommandInArray->longHelp); //there is a longHelp, but it does not have even a "@command" -> simply print the longHelp string
        }
    }
    else
    {
        char   commandAsString[51];
        size_t len=(CommandEnd-Command>50?50:(CommandEnd-Command));

        SAPDB_strncpy(commandAsString, Command, len);
        commandAsString[len]='\0';

        FUNCTION_DBG_MCN00_1("DBMSrvCmd_Explain::Explain");
        teo200_EventList aEvent(FUNCTION_NAME_MCN00_1, ERR_COMMAND_CN00_1, commandAsString);

        Reply.startWithEventList(aEvent);
    }

    return OK_CN00;
}
