/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
#include "cmCPackIFWRepository.h"

#include "cmCPackIFWGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmSystemTools.h"
#include "cmXMLParser.h"
#include "cmXMLWriter.h"

#include <stddef.h>

cmCPackIFWRepository::cmCPackIFWRepository()
: Update(cmCPackIFWRepository::None)
{}

bool
cmCPackIFWRepository::IsValid() const
{
    bool valid = true;

    switch(this->Update)
    {
        case cmCPackIFWRepository::None:
            valid = !this->Url.empty();
            break;
        case cmCPackIFWRepository::Add:
            valid = !this->Url.empty();
            break;
        case cmCPackIFWRepository::Remove:
            valid = !this->Url.empty();
            break;
        case cmCPackIFWRepository::Replace:
            valid = !this->OldUrl.empty() && !this->NewUrl.empty();
            break;
    }

    return valid;
}

bool
cmCPackIFWRepository::ConfigureFromOptions()
{
    // Name;
    if(this->Name.empty())
    {
        return false;
    }

    std::string prefix = "CPACK_IFW_REPOSITORY_" +
                         cmsys::SystemTools::UpperCase(this->Name) + "_";

    // Update
    if(this->IsOn(prefix + "ADD"))
    {
        this->Update = cmCPackIFWRepository::Add;
    } else if(IsOn(prefix + "REMOVE"))
    {
        this->Update = cmCPackIFWRepository::Remove;
    } else if(IsOn(prefix + "REPLACE"))
    {
        this->Update = cmCPackIFWRepository::Replace;
    } else
    {
        this->Update = cmCPackIFWRepository::None;
    }

    // Url
    if(const char* url = this->GetOption(prefix + "URL"))
    {
        this->Url = url;
    } else
    {
        this->Url.clear();
    }

    // Old url
    if(const char* oldUrl = this->GetOption(prefix + "OLD_URL"))
    {
        this->OldUrl = oldUrl;
    } else
    {
        this->OldUrl.clear();
    }

    // New url
    if(const char* newUrl = this->GetOption(prefix + "NEW_URL"))
    {
        this->NewUrl = newUrl;
    } else
    {
        this->NewUrl.clear();
    }

    // Enabled
    if(this->IsOn(prefix + "DISABLED"))
    {
        this->Enabled = "0";
    } else
    {
        this->Enabled.clear();
    }

    // Username
    if(const char* username = this->GetOption(prefix + "USERNAME"))
    {
        this->Username = username;
    } else
    {
        this->Username.clear();
    }

    // Password
    if(const char* password = this->GetOption(prefix + "PASSWORD"))
    {
        this->Password = password;
    } else
    {
        this->Password.clear();
    }

    // DisplayName
    if(const char* displayName = this->GetOption(prefix + "DISPLAY_NAME"))
    {
        this->DisplayName = displayName;
    } else
    {
        this->DisplayName.clear();
    }

    return this->IsValid();
}

/** \class cmCPackeIFWUpdatesPatcher
 * \brief Helper class that parses and patch Updates.xml file (QtIFW)
 */
class cmCPackeIFWUpdatesPatcher : public cmXMLParser
{
public:
    cmCPackeIFWUpdatesPatcher(cmCPackIFWRepository* r, cmXMLWriter& x)
    : repository(r)
    , xout(x)
    , patched(false)
    {}

    cmCPackIFWRepository* repository;
    cmXMLWriter&          xout;
    bool                  patched;

protected:
    void StartElement(const std::string& name, const char** atts) override
    {
        this->xout.StartElement(name);
        this->StartFragment(atts);
    }

    void StartFragment(const char** atts)
    {
        for(size_t i = 0; atts[i]; i += 2)
        {
            const char* key   = atts[i];
            const char* value = atts[i + 1];
            this->xout.Attribute(key, value);
        }
    }

    void EndElement(const std::string& name) override
    {
        if(name == "Updates" && !this->patched)
        {
            this->repository->WriteRepositoryUpdates(this->xout);
            this->patched = true;
        }
        this->xout.EndElement();
        if(this->patched)
        {
            return;
        }
        if(name == "Checksum")
        {
            this->repository->WriteRepositoryUpdates(this->xout);
            this->patched = true;
        }
    }

    void CharacterDataHandler(const char* data, int length) override
    {
        std::string content(data, data + length);
        if(content.empty() || content == " " || content == "  " ||
           content == "\n")
        {
            return;
        }
        this->xout.Content(content);
    }
};

bool
cmCPackIFWRepository::PatchUpdatesXml()
{
    // Lazy directory initialization
    if(this->Directory.empty() && this->Generator)
    {
        this->Directory = this->Generator->toplevel;
    }

    // Filenames
    std::string updatesXml = this->Directory + "/repository/Updates.xml";
    std::string updatesPatchXml =
        this->Directory + "/repository/UpdatesPatch.xml";

    // Output stream
    cmGeneratedFileStream fout(updatesPatchXml);
    cmXMLWriter           xout(fout);

    xout.StartDocument();

    this->WriteGeneratedByToStrim(xout);

    // Patch
    {
        cmCPackeIFWUpdatesPatcher patcher(this, xout);
        patcher.ParseFile(updatesXml.data());
    }

    xout.EndDocument();

    fout.Close();

    return cmSystemTools::RenameFile(updatesPatchXml.data(), updatesXml.data());
}

void
cmCPackIFWRepository::WriteRepositoryConfig(cmXMLWriter& xout)
{
    xout.StartElement("Repository");

    // Url
    xout.Element("Url", this->Url);
    // Enabled
    if(!this->Enabled.empty())
    {
        xout.Element("Enabled", this->Enabled);
    }
    // Username
    if(!this->Username.empty())
    {
        xout.Element("Username", this->Username);
    }
    // Password
    if(!this->Password.empty())
    {
        xout.Element("Password", this->Password);
    }
    // DisplayName
    if(!this->DisplayName.empty())
    {
        xout.Element("DisplayName", this->DisplayName);
    }

    xout.EndElement();
}

void
cmCPackIFWRepository::WriteRepositoryUpdate(cmXMLWriter& xout)
{
    xout.StartElement("Repository");

    switch(this->Update)
    {
        case cmCPackIFWRepository::None:
            break;
        case cmCPackIFWRepository::Add:
            xout.Attribute("action", "add");
            break;
        case cmCPackIFWRepository::Remove:
            xout.Attribute("action", "remove");
            break;
        case cmCPackIFWRepository::Replace:
            xout.Attribute("action", "replace");
            break;
    }

    // Url
    if(this->Update == cmCPackIFWRepository::Add ||
       this->Update == cmCPackIFWRepository::Remove)
    {
        xout.Attribute("url", this->Url);
    } else if(Update == cmCPackIFWRepository::Replace)
    {
        xout.Attribute("oldUrl", this->OldUrl);
        xout.Attribute("newUrl", this->NewUrl);
    }
    // Enabled
    if(!this->Enabled.empty())
    {
        xout.Attribute("enabled", this->Enabled);
    }
    // Username
    if(!this->Username.empty())
    {
        xout.Attribute("username", this->Username);
    }
    // Password
    if(!this->Password.empty())
    {
        xout.Attribute("password", this->Password);
    }
    // DisplayName
    if(!this->DisplayName.empty())
    {
        xout.Attribute("displayname", this->DisplayName);
    }

    xout.EndElement();
}

void
cmCPackIFWRepository::WriteRepositoryUpdates(cmXMLWriter& xout)
{
    if(!this->RepositoryUpdate.empty())
    {
        xout.StartElement("RepositoryUpdate");
        for(cmCPackIFWRepository* r : this->RepositoryUpdate)
        {
            r->WriteRepositoryUpdate(xout);
        }
        xout.EndElement();
    }
}
