#include <iostream>
#include <vector>
#include <fstream>
#include "mns/mns_client.h"

#ifdef _WIN32
#include <windows.h>
#endif

using namespace std;
using namespace mns::sdk;

string trim(string& s)
{
    int n;
    for (n = s.size() - 1; n >= 0; n--)
    {
        if (s[n] != ' ' && s[n] != '\t' && s[n] != '\n' && s[n] != '\r')
        {
            break;    
        }
    }
    return s.substr(0, n+1);
}

void readConfig(string& endpoint, string& accessId, string& accessKey, string& stsToken)
{
    stsToken = "";
    ifstream is_file("aliyun-mns.properties");
	if (!is_file.is_open())
	{
		cout << "Cannot find config: aliyun-mns.properties" << endl;
		return;
	}
    std::string line;
    while( std::getline(is_file, line) )
    {
        std::istringstream is_line(line);
        std::string key;
        if( std::getline(is_line, key, '=') )
        {
            std::string value;
            if( std::getline(is_line, value) )
            {
                if (key == "mns.accountendpoint")
                {
                    endpoint = trim(value);
                }
                else if (key == "mns.accesskeyid")
                {
                    accessId = trim(value);
                }
                else if (key == "mns.accesskeysecret")
                {
                    accessKey = trim(value);
                }
                else if (key == "mns.ststoken")
                {
                    stsToken = trim(value);
                }
            }
        }
    }
}

int main()
{
    string endpoint = "";
    string accessId = "";
    string accessKey = "";
    string stsToken = "";
    readConfig(endpoint, accessId, accessKey, stsToken);
	if (endpoint == "")
	{
		cout << "Endpoint Empty!" << endl;
		return -1;
	}
    MNSClient mnsClient(endpoint, accessId, accessKey, stsToken);

    TopicPtr topicPtr;
    try
    {
        mnsClient.deleteTopic("TestCplusplusTopic");
        topicPtr = mnsClient.createTopic("TestCplusplusTopic");
        cout << "TopicName: " << topicPtr->getTopicName() << endl;
    }
    catch (MNSServerException& me)
    {
        cout << "CreateTopic Failed: " + me.GetErrorCode() << endl;
        // handle exception according to the errorcode
        return -1;
    }
    catch (MNSExceptionBase& mb)
    {
        cout << "CreateTopic Failed: " + mb.ToString() << endl;
        // log it and retry
        return -2;
    }

    // set logging enabled
    try
    {
        TopicAttributes attributes;
        topicPtr->getAttributes(attributes);
        cout << "LoggingEnabled: "
             << attributes.isLoggingEnabled() << endl;

        TopicAttributes attributes2;
        attributes2.setLoggingEnabled(true);
        topicPtr->setAttributes(attributes2);
        topicPtr->getAttributes(attributes);
        cout << "LoggingEnabled: "
             << attributes.isLoggingEnabled() << endl;

        TopicAttributes attributes3;
        topicPtr->setAttributes(attributes3);
        topicPtr->getAttributes(attributes);
        cout << "LoggingEnabled: "
             << attributes.isLoggingEnabled() << endl;

        TopicAttributes attributes4;
        attributes4.setLoggingEnabled(false);
        topicPtr->setAttributes(attributes4);
        topicPtr->getAttributes(attributes);
        cout << "LoggingEnabled: "
             << attributes.isLoggingEnabled() << endl;
    }
    catch (MNSServerException& me)
    {
        cout << "setAttributes Failed: " + me.GetErrorCode() << endl;
        // handle exception according to the errorcode
        return -1;
    }
    catch (MNSExceptionBase& mb)
    {
        cout << "setAttributes Failed: " + mb.ToString() << endl;
        // log it and retry
        return -2;
    }

    try
    {
        mnsClient.createTopic("TestCplusplus1");
        mnsClient.createTopic("TestCplusplus2");
        mnsClient.createTopic("TestCplusplus3");
        mnsClient.createTopic("TestCplusplus4");
        mnsClient.createTopic("TestCplusplus5");
        vector<string> topicNames;
        string nextMarker;
        ListTopicRequest req("TestCplusplus", "", 2);
        mnsClient.listTopic(req, topicNames, nextMarker);
        for (int i = 0; i < topicNames.size(); ++i)
        {
            cout << topicNames[i] << endl;
        }
        cout << nextMarker << endl;
        mnsClient.deleteTopic("TestCplusplus1");
        mnsClient.deleteTopic("TestCplusplus2");
        mnsClient.deleteTopic("TestCplusplus3");
        mnsClient.deleteTopic("TestCplusplus4");
        mnsClient.deleteTopic("TestCplusplus5");

        topicPtr = mnsClient.getTopicRef("TestCplusplusTopic");

        TopicAttributes attributes;
        topicPtr->getAttributes(attributes);
        cout << "MaximumMessageSize: "
             << attributes.getMaximumMessageSize() << endl;

        attributes.setMaximumMessageSize(1024);
        topicPtr->setAttributes(attributes);

        topicPtr->getAttributes(attributes);
        cout << "After set, MaximumMessageSize: "
             << attributes.getMaximumMessageSize() << endl;

        // single message
        PublishMessageResponse pmResp;
        topicPtr->publishMessage("Test<HAHA>\"Messge", pmResp);
        cout << "PublishMessage Succeed!" << endl;

        SubscriptionAttributes sa;
        sa.setContentFormat(SIMPLIFIED);
        topicPtr->subscribe("SubscriptionName1", "http://1.1.1.1:8458", sa);
        cout << "SubscribeSucceed" << endl;

        topicPtr->getSubscriptionAttributes("SubscriptionName1", sa);
        cout << " ContentFormat: "
             << ContentFormatHelper::ContentFormatToString(sa.getContentFormat())
             << " NotifyStrategy: "
             << NotifyStrategyHelper::NotifyStrategyToString(sa.getNotifyStrategy())
             << " TopicName: "
             << sa.getTopicName() << endl;

        SubscriptionAttributes sa2;
        sa2.setNotifyStrategy(EXPONENTIAL_DECAY_RETRY);
        topicPtr->setSubscriptionAttributes("SubscriptionName1", sa2);

        cout << "After Setting the NotifyStrategy: " << endl;
        topicPtr->getSubscriptionAttributes("SubscriptionName1", sa);
        cout << " ContentFormat: "
             << ContentFormatHelper::ContentFormatToString(sa.getContentFormat())
             << " NotifyStrategy: "
             << NotifyStrategyHelper::NotifyStrategyToString(sa.getNotifyStrategy())
             << " TopicName: "
             << sa.getTopicName() << endl;

        topicPtr->publishMessage("Test<HAHA>\"Messge", pmResp);
#ifdef _WIN32
		Sleep(1000);
#else
		usleep(1000000);
#endif

        topicPtr->subscribe("SubscriptionName2", "http://1.1.1.1:8458", sa);
        topicPtr->subscribe("SubscriptionName3", "http://1.1.1.1:8458", sa);
        topicPtr->subscribe("SubscriptionName4", "http://1.1.1.1:8458", sa);
        topicPtr->subscribe("SubscriptionName5", "http://1.1.1.1:8458", sa);
        topicPtr->subscribe("SubscriptionName6", "http://1.1.1.1:8458", sa);
        std::vector<std::string> subscriptionNames;
        ListSubscriptionRequest lsReq("", "", 3);
        topicPtr->listSubscription(lsReq, subscriptionNames, nextMarker);
        cout << " Subscriptions: " << endl;
        for (int i = 0; i < subscriptionNames.size(); ++i)
        {
            cout << subscriptionNames[i] << endl;
        }
        cout << nextMarker << endl;
        topicPtr->unsubscribe("SubscriptionName2");
        topicPtr->unsubscribe("SubscriptionName3");
        topicPtr->unsubscribe("SubscriptionName4");
        topicPtr->unsubscribe("SubscriptionName5");

        topicPtr->unsubscribe("SubscriptionName1");

        subscriptionNames.clear();
        topicPtr->listSubscription("", subscriptionNames);
        cout << " After Unsubscribe, Subscriptions: " << endl;
        for (int i = 0; i < subscriptionNames.size(); ++i)
        {
            cout << subscriptionNames[i] << endl;
        }
        topicPtr->unsubscribe("SubscriptionName6");

        string queueName = "CPPSDKTestSubQueue";
        mnsClient.deleteQueue(queueName);
        QueuePtr queuePtr = mnsClient.createQueue(queueName);

        string queueEndpoint = topicPtr->generateQueueEndpoint(queueName);
        topicPtr->subscribe("SubscribeToQueue", queueEndpoint);

        topicPtr->publishMessage("Test<HAHA>\"Messge222", pmResp);

        Message topicMessage;
        queuePtr->receiveMessage(30, topicMessage);
        cout << "TopicQueueMessageBody: " << topicMessage.getMessageBody() << endl;

        topicPtr->unsubscribe("SubscribeToQueue");
        mnsClient.deleteQueue(queueName);


        {
            string mailEndpoint = topicPtr->generateMailEndpoint("a@b.com");
            cout << mailEndpoint << endl;
            topicPtr->subscribe("SubscribeToMail", mailEndpoint);

            MessageAttributes messageAttributes;
            MailAttributes* mailAttributesPtr =
                messageAttributes.getMailAttributesPtr();
            mailAttributesPtr->setSubject("TestSubject");
            mailAttributesPtr->setAccountName("TestAccountName");

            topicPtr->publishMessage("Test<HAHA>\"Messge333", messageAttributes, pmResp);
            cout << "PublishMailMessage: " << pmResp.getMessageId() << endl;
#ifdef _WIN32
			Sleep(300000);
#else
			sleep(300);
#endif
        }

        mnsClient.deleteTopic("TestCplusplusTopic");
        return 0;
    }
    catch (MNSServerException& me)
    {
        cout << "Request Failed: " + me.GetErrorCode() << endl;
        // handle exception according to the errorcode
        return -1;
    }
    catch (MNSExceptionBase& mb)
    {
        cout << "Request Failed: " + mb.ToString() << endl;
        // log it and retry
        return -2;
    }
}
