#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 = "CPPSDKTestMessageTag";
            mnsClient.deleteQueue(queueName);
            QueuePtr queuePtr = mnsClient.createQueue(queueName);

            string queueEndpoint = topicPtr->generateQueueEndpoint(queueName);
            topicPtr->subscribe("SubscribeToMessageTag", "xushan-tag", queueEndpoint);
            cout << "SubscribeSucceed  with message tag" << endl;

            topicPtr->getSubscriptionAttributes("SubscribeToMessageTag", sa);
                    cout << " SubscriptionName: "
                         << sa.getSubscriptionName()
                         << " TopicName: "
                         << sa.getTopicName()
                         << " FilterTag: "
                         << sa.getFilterTag() << endl;

            topicPtr->publishMessage("xushan------1111", pmResp);
            topicPtr->publishMessage("xushan------2222", pmResp);
            topicPtr->publishMessage("xushan------3333", "xushan-tag", pmResp);
            topicPtr->publishMessage("xushan------4444", "xushan-tag", pmResp);
            topicPtr->publishMessage("xushan------5555", "xushan-tag", pmResp);
            topicPtr->publishMessage("xushan------6666", "xushan-tag1", pmResp);
            cout << "PublishMessage with tag Succeed!" << endl;

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

            topicPtr->publishMessage("xushan------7777", "xushan-tag", pmResp);

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

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

        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 smsEndpoint = topicPtr->generateSMSEndpoint();
            cout << smsEndpoint << endl;
            topicPtr->subscribe("SubscribeToSMS", smsEndpoint);

            MessageAttributes messageAttributes;
            SMSAttributes* smsAttributesPtr =
                messageAttributes.getSMSAttributesPtr(BATCH_SMS);
            smsAttributesPtr->setSignName("测试1");
            smsAttributesPtr->setTemplateCode("SMS_3333333");
            smsAttributesPtr->setReceiver("13666666666");

            topicPtr->publishMessage("Test<HAHA>\"Messge333", messageAttributes, pmResp);
            cout << "PublishSMSMessage: " << pmResp.getMessageId() << endl;
        }

        {
            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;
    }
}
