import Rx from 'rx';
import { OpenFin } from '../system/openFin';
import { ExecuteTradeRequest, ExecuteTradeResponse } from './model';
import { TradeMapper } from './mappers';
import { logger, SchedulerService } from '../system';
import { Connection, ServiceBase } from '../system/service';
import { ReferenceDataService } from './';

const _log:logger.Logger = logger.create('ExecutionService');

export default class ExecutionService extends ServiceBase {

  static EXECUTION_TIMEOUT_MS = 2000;

  constructor(serviceType:string,
              connection:Connection,
              schedulerService:SchedulerService,
              referenceDataService:ReferenceDataService,
              openFin:OpenFin) {
    super(serviceType, connection, schedulerService);
    this._openFin = openFin;
    this._tradeMapper = new TradeMapper(referenceDataService);
  }

  executeTrade(executeTradeRequest:ExecuteTradeRequest):Rx.Observable<ExecuteTradeResponse> {
    let _this = this;
    return Rx.Observable.create(
      o => {
        _log.info(`executing: ${executeTradeRequest.toString()}`, executeTradeRequest);
        let disposables = new Rx.CompositeDisposable();

        disposables.add(
          _this._openFin
            .checkLimit(executeTradeRequest.SpotRate, executeTradeRequest.Notional, executeTradeRequest.CurrencyPair)
            .take(1)
            .subscribe(limitCheckResult => {
              if (limitCheckResult) {
                disposables.add(
                  _this._serviceClient
                    .createRequestResponseOperation('executeTrade', executeTradeRequest)
                    .map(dto => {
                      const trade = _this._tradeMapper.mapFromTradeDto(dto.Trade);
                      _log.info(`execute response received for: ${executeTradeRequest.toString()}. Status: ${trade.status}`, dto);
                      return ExecuteTradeResponse.create(trade);
                    })
                    .timeout(ExecutionService.EXECUTION_TIMEOUT_MS, Rx.Observable.return(ExecuteTradeResponse.createForError('Trade execution timeout exceeded')))
                    .subscribe(o)
                );
              }
              else {
                o.onNext(ExecuteTradeResponse.createForError('Credit limit exceeded'));
              }
            })
        );
        return disposables;
      }
    );
  }
}

@@ -463,32 +463,39 @@
 tatic EXECUTION_
+CLIENT_
 TIMEOUT_MS = 200
@@ -493,11 +493,60 @@
 _MS 
-= 2
+ =  2000;%0A  static EXECUTION_REQUEST_TIMEOUT_MS = 30
 000;
@@ -1507,42 +1507,21 @@
     
-disposables.add(%0A                 
+let request =
  _th
@@ -1538,18 +1538,16 @@
 eClient%0A
-  
         
@@ -1625,16 +1625,178 @@
 equest)%0A
+                  .publish()%0A                  .refCount();%0A                disposables.add(%0A                  Rx.Observable.merge(%0A                    request%0A  
         
@@ -1843,16 +1843,18 @@
         
+ 
+ 
 const tr
@@ -1902,24 +1902,26 @@
 dto.Trade);%0A
+  
             
@@ -2039,16 +2039,18 @@
 , dto);%0A
+  
         
@@ -2118,34 +2118,436 @@
                 
-%7D)
+  %7D),%0A                    // show timeout error if request is taking longer than expected%0A                    Rx.Observable.timer(ExecutionService.EXECUTION_CLIENT_TIMEOUT_MS)%0A                      .map(() =%3E ExecuteTradeResponse.createForError('Trade execution timeout exceeded'))%0A                      .takeUntil(request))%0A                    // if we never receive a response, mark request as complete
 %0A               
@@ -2587,16 +2587,24 @@
 ECUTION_
+REQUEST_
 TIMEOUT_

handle execution requests taking longer than expected