//
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
//
//  See accompanying file COPYING.TXT file for licensing details.
//
#include <cppcms/application.h>
#include <cppcms/url_dispatcher.h>
#include <cppcms/applications_pool.h>
#include <cppcms/service.h>
#include <cppcms/http_response.h>
#include <cppcms/http_request.h>
#include <cppcms/http_context.h>
#include <booster/intrusive_ptr.h>
#include <booster/aio/deadline_timer.h>
#include <booster/system_error.h>

#include <set>

class ticker : public cppcms::application {
public:
    ticker(cppcms::service &srv) : 
        cppcms::application(srv),
        counter_(0),
        price_(1.2),
        tm_(srv.get_io_service())
    {
        wait();
    }

    void wait()
    {
        tm_.expires_from_now(booster::ptime::from_number(double(rand())/RAND_MAX + 0.01));
        tm_.async_wait([=](booster::system::error_code const &e){
            if(!e) {
                on_timer();
                wait();
            }
        });
    }

    void on_timer()
    {
        double new_price = price_ + double(rand()) / RAND_MAX * 2.0 - 1;
        if(new_price <= 0.01)
            new_price = 0.01;
        update_price(new_price);
    }
    void update_price(double new_one)
    {
        counter_++;
        price_ = new_one;
        for(auto waiter : waiters_) {
            async_send(waiter);
        }
        waiters_.clear();
    }
    void main(std::string /*url*/)
    {
        response().set_content_header("text/event-stream");
        response().set_header("Cache-Control", "no-cache");

        auto last_id = atoi(request().cgetenv("HTTP_LAST_EVENT_ID"));

        auto context=release_context();

        context->async_on_peer_reset([=](){
            this->waiters_.erase(context);
        });

        if(last_id != counter_) {
            async_send(context);
        }
        else
            waiters_.insert(context);
    }

    void async_send(booster::shared_ptr<cppcms::http::context> waiter)
    {
        waiter->response().out() <<
            "id:" <<  counter_ <<"\n"
            "data:" << price_ << "\n"
            "\n";
        waiter->async_flush_output([=,counter_](cppcms::http::context::completion_type status){
            if(status!=0)
                return;
            if(counter_ != this->counter_) {
                this->async_send(waiter);
            }
            else {
                this->waiters_.insert(waiter);
            }
        });
        
    }
    
private:
    int counter_;
    double price_;
    typedef std::set<booster::shared_ptr<cppcms::http::context> > waiters_type;
    waiters_type waiters_;
    booster::aio::deadline_timer tm_;
};


int main(int argc,char **argv)
{
    try {
        cppcms::service service(argc,argv);
        booster::intrusive_ptr<ticker> c=new ticker(service);
        service.applications_pool().mount(c);
        service.run();
    }
    catch(std::exception const &e) {
        std::cerr<<"Catched exception: "<<e.what()<<std::endl;
        return 1;
    }
    return 0;
}
// vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4


