/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <pwd.h>
//#include <time.h>

#include "Assertions.hpp"
#include "MacroVisitor.h"
#include "MvPath.hpp"
#include "UtilitiesC.h"
#include "SuperPage.h"
#include "Page.h"
#include "ObjectInfo.h"

// --  Static method
// --  Establishes the new macro file Name
std::string
MacroVisitor::CreateFileName(const MvRequest& superpageRequest)
{
    // Create an appropriate fileName
    std::string pathName = ObjectInfo::ObjectFullPath(superpageRequest);
    return {MakeIconName(pathName.c_str(), "MacroFramework")};
}

// -- Constructor
//
//    Creates a new Macro file
MacroVisitor::MacroVisitor(std::string& fileName, const MvRequest& plotReq) :
    outFileName_(fileName),
    plotReq_(plotReq)
{
    vdMap_.clear();

    // Create the temporary file
    tmpFileName_ = MakeTmpName("MacroTmp");

    // Open the output file (the destructor will close it)
    if (FileCanBeOpened(tmpFileName_.c_str(), "w") == true)
        filePtr_ = fopen(tmpFileName_.c_str(), "w");

    ensure(filePtr_ != nullptr);

    // Set the Prolog information in the output file
    WriteProlog();
}

//  -- Destructor
//
//     Closes the file and performs additional processing
//     (creating an icon or printing)

MacroVisitor::~MacroVisitor()
{
    require(filePtr_ != nullptr);

    // Write the trailer information on the file
    this->WriteTrailer();

    fclose(filePtr_);  // close the output file

    // Move temporary file to the output file
    char buf[1024];
    sprintf(buf, "mv '%s' '%s'", tmpFileName_.c_str(), outFileName_.c_str());
    system(buf);
}

// -- METHOD: WriteProlog
//
// -- PURPOSE: write the header of the macro program
//
void MacroVisitor::WriteProlog()
{
    struct passwd* who = getpwuid(getuid());
    time_t when = time(nullptr);
    const char* version = getenv("METVIEW_VERSION");

    fprintf(filePtr_, "# Metview Macro - created from Display Window contents\n");
    fprintf(filePtr_, "# By:   %s\n", version);
    fprintf(filePtr_, "# Date: %s", ctime(&when));
    fprintf(filePtr_, "# For:  %s (%s)\n", who->pw_name, who->pw_gecos);
    fprintf(filePtr_, "# \n");
    fprintf(filePtr_, "# Disclaimer:\n");
    fprintf(filePtr_, "#   This macro is to be used primarily as a framework.\n");
    fprintf(filePtr_, "#   Some macros generated in this manner may not work\n");
    fprintf(filePtr_, "#   as expected and in such cases you may need to\n");
    fprintf(filePtr_, "#   fine-tune the macro manually!\n");
}

// -- METHOD: WriteTrailer
//
// -- PURPOSE: write the final part of the macro program
//
void MacroVisitor::WriteTrailer()
{
    // Set the device Information
    deviceInfo_.WriteDescription(filePtr_);

    //   fprintf(filePtr_,"\n# Drops to SuperPage");
    //   superpageDrops_.WriteDescription ( filePtr_ );

    fprintf(filePtr_, "\n# Icons description");
    pageDrops_.WriteDescription(filePtr_);

    // If there are no drops associated to the pages,
    // we still need to plot something
    if (pageDrops_.HasAction() == false && superpageDrops_.HasAction() == false) {
        // Set build layout function
        fprintf(filePtr_, "# Call function to build layout (defined at end of macro)");
        fprintf(filePtr_, "dw = build_layout()\n");

        // Write the plot command
        fprintf(filePtr_, "plot ( dw )\n");
    }

    fprintf(filePtr_, "\n# End of main program\n");

    // Output the page Drops and Contents
    fprintf(filePtr_, "\n# Function to build the layout\n");
    fprintf(filePtr_, "function build_layout()\n\n");

    // Add some indentation since this is now in a function
    pageInfo_.LinePrefix("   ");
    superpageInfo_.LinePrefix("   ");

    pageInfo_.WriteDescription(filePtr_);

    superpageInfo_.WriteDescription(filePtr_);
    fprintf(filePtr_, "\nend build_layout\n");
}

// -- METHOD: Visit ( SuperPage& )
//
// -- PURPOSE: Visit a superpage and retrieve its creation request,
//			   name, and all dataunits and visdefs associated to it
//

void MacroVisitor::Visit(SuperPage& superpage)
{
    macroName_ = superpage.MacroName();

    superpage.DescribeDevice(deviceInfo_);

    superpage.DescribeYourself(superpageInfo_);

    //   superpage.DescribeDrops ( superpageDrops_, vdMap_, soMap_ );
}

// -- METHOD: Visit ( Page& )
//
// -- PURPOSE: Visit a page and retrieve its creation request,
//	name, and all dataunits and visdefs associated to it
//
void MacroVisitor::Visit(Page& page)
{
    // Print the contents of the page
    page.DescribeYourself(pageInfo_);

    // Get the icons dropped into this page
    MvRequest iconsReq;
    if (!this->getIconsByPageId(page.Id(), iconsReq))
        return;

    // PrintData Units and VisDefs
    //   page.DescribeDrops ( pageDrops_, vdMap_ );
    page.DescribeDrops(pageDrops_, iconsReq);
}

void MacroVisitor::Visit(SubPage&)
{
    // Empty
}

bool MacroVisitor::getIconsByPageId(const int id, MvRequest& out)
{
    // Search for the targeted Page request.
    // If found then advance the pointer to the next request
    const std::string pageVerb("PAGE");
    plotReq_.rewind();
    while (plotReq_) {
        if ((const char*)plotReq_.getVerb() == pageVerb && (int)plotReq_("_ID") == id) {
            plotReq_.advance();
            break;
        }

        plotReq_.advance();
    }

    // Targeted Page found
    // Get requests:
    //    a) skip LAYER request
    //    b) unfold VISDEFS request. This will make the
    //       algorithm to extract the icons easier
    const std::string layerVerb("LAYER");
    const std::string visdefsVerb("VISDEFS");
    while (plotReq_ && plotReq_.getVerb() != pageVerb) {
        // Handle VISDEFS request. Save all ACTIONS requests.
        if (plotReq_.getVerb() == visdefsVerb)
            out += plotReq_.getSubrequest("ACTIONS");

        // Handle non LAYER request. Save it.
        else if (plotReq_.getVerb() != layerVerb)
            out += plotReq_.justOneRequest();

        plotReq_.advance();
    }

    return (out ? true : false);
}
