public class CallStats {

  /** The http client. */
  private CallStatsHttp2Client httpClient;

  /** The app id. */
  private int appId;

  /** The bridge id. */
  private String bridgeId;

  /** The listener. */
  private CallStatsInitListener listener;

  /** The authenticator. */
  private CallStatsAuthenticator authenticator;

  /** The logger. */
  private static final Logger logger = Logger.getLogger("CallStats");

  /** The gson. */
  private Gson gson;

  /** The server info. */
  private ServerInfo serverInfo;

  /** The is initialized. */
  private boolean isInitialized;

  private BridgeStatusInfoQueue bridgeStatusInfoQueue;

  private Map<String, List<ConferenceStats>> conferenceStatsMap =
      new HashMap<String, List<ConferenceStats>>();

  /** The bridge keep alive manager. */
  private CallStatsBridgeKeepAliveManager bridgeKeepAliveManager;
  
  private CallStatsConferenceAliveManager conferenceKeepAliveManager;

  private ICallStatsTokenGenerator tokenGenerator;

  private CallStatsHttp2Client authHttpClient;
  
  private CallStatsHttp2Client conferenceAliveHttpClient;

  /**
   * Checks if is initialized.
   *
   * @return true, if is initialized
   */
  public boolean isInitialized() {
    return isInitialized;
  }

  /**
   * Sets the initialized.
   *
   * @param isInitialized the new initialized
   */
  private void setInitialized(boolean isInitialized) {
    this.isInitialized = isInitialized;
  }

  /**
   * Instantiates a new callstats.
   */
  public CallStats() {
    gson = new Gson();
    bridgeStatusInfoQueue = new BridgeStatusInfoQueue();
    if (System.getProperty("callstats.configurationFile") != null) {
      CallStatsConst.CallStatsJavaSDKPropertyFileName =
          System.getProperty("callstats.configurationFile");
    }

    logger.info("config file path is " + System.getProperty("callstats.configurationFile") + ":"
        + CallStatsConst.CallStatsJavaSDKPropertyFileName);
    CallStatsConst.CS_VERSION = getClass().getPackage().getImplementationVersion();
  }

  private String getToken() {
    return authenticator.getToken();
  }

  /**
   * Initialize callstats.
   *
   * @param appId the app id
   * @param appSecret the app secret
   * @param bridgeId the bridge id
   * @param serverInfo the server info
   * @param callStatsInitListener the call stats init listener
   */

  public void initialize(final int appId, final String appSecret, final String bridgeId,
      final ServerInfo serverInfo, final CallStatsInitListener callStatsInitListener) {
    if (StringUtils.isBlank(appSecret)) {
      logger.severe("intialize: Arguments cannot be null ");
      throw new IllegalArgumentException("intialize: Arguments cannot be null");
    }
    initialize(appId, new TokenGeneratorHs256(appSecret.toCharArray(), appId, bridgeId), bridgeId,
        serverInfo, callStatsInitListener);
  }

  /**
   * Initialize callstats.
   *
   * @param appId the app id
   * @param tokenGenerator token generator
   * @param bridgeId the bridge id
   * @param serverInfo the server info
   * @param callStatsInitListener the call stats init listener
   */

  public void initialize(final int appId, ICallStatsTokenGenerator tokenGenerator,
      final String bridgeId, final ServerInfo serverInfo,
      final CallStatsInitListener callStatsInitListener) {
    if (appId <= 0 || StringUtils.isBlank(bridgeId) || serverInfo == null
        || callStatsInitListener == null) {
      logger.severe("intialize: Arguments cannot be null ");
      throw new IllegalArgumentException("intialize: Arguments cannot be null");
    }

    this.appId = appId;
    this.tokenGenerator = tokenGenerator;
    this.bridgeId = bridgeId;
    this.listener = callStatsInitListener;
    this.serverInfo = serverInfo;
    CallStatsConfigProvider.init();

    httpClient = new CallStatsHttp2Client(CallStatsConfigProvider.connectionTimeOut);
    authHttpClient = new CallStatsHttp2Client(CallStatsConfigProvider.connectionTimeOut);
    conferenceAliveHttpClient = new CallStatsHttp2Client(CallStatsConfigProvider.connectionTimeOut);
    
    authenticator = new CallStatsAuthenticator(appId, this.tokenGenerator, bridgeId, authHttpClient,
        new CallStatsInitListener() {
          public void onInitialized(String msg) {
            setInitialized(true);
            logger.info("SDK Initialized " + msg);
            startKeepAliveThread();
            listener.onInitialized(msg);
          }

          public void onError(CallStatsErrors error, String errMsg) {
            logger.info("SDK Initialization Failed " + errMsg);
            listener.onError(error, errMsg);;
          }
        });
    authenticator.doAuthentication();
  }
  
  /**
   * Start the conference Alive sender
   *
   * @param originID initiator identifier
   * @param confID conference identifier
   * @param ucID ucID obtained from conference creation
   */
  
  public void startConferenceAliveSender(String originID, String confID, String ucID) {
    if (conferenceKeepAliveManager == null) {
    conferenceKeepAliveManager = new CallStatsConferenceAliveManager(appId, bridgeId,
    authenticator.getToken(), conferenceAliveHttpClient, new CallStatsBridgeKeepAliveStatusListener() {
        public void onKeepAliveError(CallStatsErrors error, String errMsg) {
          if (error == CallStatsErrors.AUTH_ERROR) {
            authenticator.doAuthentication();
          }
        }

        public void onSuccess() {
        }
      });
    }
    long apiTS = System.currentTimeMillis();
    FabricSetupEvent eventMessage = new FabricSetupEvent(bridgeId, originID, "jitsi", apiTS);
    String requestMessageString = gson.toJson(eventMessage);
    String url = "";
    try {
      url =
          "/" + appId + "/conferences/" + URLEncoder.encode(confID, "utf-8") + "/" + ucID + "/events/fabric/setup";
    } catch (UnsupportedEncodingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    sendCallStatsConferenceEventMessage(url, requestMessageString, null);
    conferenceKeepAliveManager.startConferenceAliveSender(confID, ucID, authenticator.getToken());
  }
  
  /**
   * Stop the conference Alive sender
   *
   * @param ucID ucID obtained from conference creation
   */
  
  public void stopConferenceAliveSender(String ucID) {
    if (conferenceKeepAliveManager == null) {
      return;
    }
    conferenceKeepAliveManager.stopConferenceAliveSender(ucID);
  }

  /**
   * Send call stats bridge status update.
   *
   * @param bridgeStatusInfo the bridge status info
   */
  public void sendCallStatsBridgeStatusUpdate(BridgeStatusInfo bridgeStatusInfo) {
    if (!isInitialized()) {
      bridgeStatusInfoQueue.push(bridgeStatusInfo);
      return;
    }

    long epoch = System.currentTimeMillis();
    String token = getToken();
    BridgeStatusUpdateMessage eventMessage =
        new BridgeStatusUpdateMessage(bridgeId, epoch, bridgeStatusInfo);
    String requestMessageString = gson.toJson(eventMessage);
    String url = "/" + appId + "/stats/bridge/status";
    httpClient.sendBridgeStatistics(url, token, requestMessageString,
        new CallStatsHttp2ResponseListener() {
          public void onResponse(Response response) {
            int responseStatus = response.code();
            BridgeStatusUpdateResponse eventResponseMessage;
            try {
              String responseString = response.body().string();
              eventResponseMessage =
                  gson.fromJson(responseString, BridgeStatusUpdateResponse.class);
            } catch (IOException e) {
              logger.log(Level.SEVERE, "IO Exception " + e.getMessage(), e);
              throw new RuntimeException(e);
            } catch (JsonSyntaxException e) {
              logger.log(Level.SEVERE, "Json Syntax Exception " + e.getMessage(), e);
              e.printStackTrace();
              throw new RuntimeException(e);
            }
            logger.fine("BridgeStatusUpdate Response " + eventResponseMessage.getStatus() + ":"
                + eventResponseMessage.getMsg());
            httpClient.setDisrupted(false);
            if (responseStatus == CallStatsResponseStatus.RESPONSE_STATUS_SUCCESS) {
              sendCallStatsBridgeStatusUpdateFromQueue();
            } else if (responseStatus == CallStatsResponseStatus.INVALID_AUTHENTICATION_TOKEN) {
              bridgeKeepAliveManager.stopKeepAliveSender();
              authenticator.doAuthentication();
            } else {
              httpClient.setDisrupted(true);
            }
          }

          public void onFailure(Exception e) {
            logger.log(Level.SEVERE, "Response exception" + e.getMessage(), e);
            httpClient.setDisrupted(true);
          }

        });
  }

  private synchronized void sendCallStatsBridgeStatusUpdateFromQueue() {
    if (bridgeStatusInfoQueue.getLength() < 1)
      return;

    while (bridgeStatusInfoQueue.getLength() > 0) {
      BridgeStatusInfo bridgeStatusInfo = bridgeStatusInfoQueue.pop();
      sendCallStatsBridgeStatusUpdate(bridgeStatusInfo);
    }
  }

  public synchronized void sendCallStatsConferenceEvent(CallStatsConferenceEvents eventType,
      ConferenceInfo conferenceInfo, final CallStatsStartConferenceListener listener) {
    if (eventType == null || conferenceInfo == null || listener == null) {
      logger.severe("sendCallStatsConferenceEvent: Arguments cannot be null ");
      throw new IllegalArgumentException("sendCallStatsConferenceEvent: Arguments cannot be null");
    }

    long apiTS = System.currentTimeMillis();
    if (eventType == CallStatsConferenceEvents.CONFERENCE_SETUP) {
      ConferenceSetupEvent eventMessage =
          new ConferenceSetupEvent(bridgeId, conferenceInfo.getInitiatorID(), conferenceInfo.getInitiatorSiteID(), apiTS, serverInfo);
      String requestMessageString = gson.toJson(eventMessage);
      String url = "";
      try {
        url =
            "/" + appId + "/conferences/" + URLEncoder.encode(conferenceInfo.getConfID(), "utf-8");
      } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      sendCallStatsConferenceEventMessage(url, requestMessageString, listener);
    } else {
      listener.onError(CallStatsErrors.CS_PROTO_ERROR, "Invaid Message type");
    }
  }

  public synchronized void sendCallStatsConferenceEvent(CallStatsConferenceEvents eventType,
      UserInfo userInfo) {
    if (eventType == null || userInfo == null) {
      logger.severe("sendCallStatsConferenceEvent: Arguments cannot be null ");
      throw new IllegalArgumentException("sendCallStatsConferenceEvent: Arguments cannot be null");
    }

    // long apiTS = System.currentTimeMillis();
    // String token = getToken();
    if (eventType == CallStatsConferenceEvents.AUDIO_MUTE
        || eventType == CallStatsConferenceEvents.AUDIO_UNMUTE
        || eventType == CallStatsConferenceEvents.VIDEO_PAUSE
        || eventType == CallStatsConferenceEvents.VIDEO_RESUME) {
      // TODO send media action event
    } else if (eventType == CallStatsConferenceEvents.USER_JOINED
        || eventType == CallStatsConferenceEvents.USER_LEFT) {
      // TODO send user action event
    } else if (eventType == CallStatsConferenceEvents.FABRIC_HOLD
        || eventType == CallStatsConferenceEvents.FABRIC_RESUME) {
      // TODO send fabric action event
    } else if (eventType == CallStatsConferenceEvents.CONFERENCE_SETUP_FAILED
        || eventType == CallStatsConferenceEvents.CONFERENCE_TERMINATED) {
      // TODO send conference action event
    }
    // sendCallStatsConferenceEventMessage(userInfo.getConfID(), requestMessageString, null);
  }

  private synchronized void sendCallStatsConferenceEventMessage(String url, String reqMsg,
      final CallStatsStartConferenceListener listener) {
    String token = getToken();
    if (token == null) {
      logger.severe("sendCallStatsConferenceEvent: Not Initialized/Token Unavaialble");
      return;
    }

    httpClient.sendBridgeEvents(url, token, reqMsg, new CallStatsHttp2ResponseListener() {
      public void onResponse(Response response) {
        int responseStatus = response.code();
        String responseString = "";
        try {
          responseString = response.body().string();
        } catch (IOException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
        }
        logger.fine("received response " + responseString);
        if (responseStatus == CallStatsResponseStatus.RESPONSE_STATUS_SUCCESS) {
          CallStatsEventResponse eventResponseMessage;
          try {
            eventResponseMessage = gson.fromJson(responseString, CallStatsEventResponse.class);
          } catch (JsonSyntaxException e) {
            logger.log(Level.SEVERE, "Json Syntax Exception " + e.getMessage(), e);
            e.printStackTrace();
            throw new RuntimeException(e);
          }
          logger.fine("conference event Response status is " + eventResponseMessage.getStatus()
              + ":" + eventResponseMessage.getUcID());
          if (listener != null) {
            listener.onResponse(eventResponseMessage.getUcID());
          }
          httpClient.setDisrupted(false);
        } else {

          httpClient.setDisrupted(true);
        }
      }

      public void onFailure(Exception e) {
        logger.log(Level.SEVERE, "Response exception" + e.getMessage(), e);
        httpClient.setDisrupted(true);
      }

    });
  }

  public synchronized void startStatsReportingForUser(String userID, String confID) {
    if (userID == null || confID == null) {
      logger.severe("startStatsReportingForUser: Arguments cannot be null ");
      throw new IllegalArgumentException("startStatsReportingForUser: Arguments cannot be null");
    }

    String key = userID + ":" + confID;

    List<ConferenceStats> tempStats = conferenceStatsMap.get(key);
    if (tempStats == null) {
      tempStats = new ArrayList<ConferenceStats>();
      conferenceStatsMap.put(key, tempStats);
    }
  }

  public synchronized void stopStatsReportingForUser(String userID, String confID) {
    if (userID == null || confID == null) {
      logger.severe("stopStatsReportingForUser: Arguments cannot be null ");
      throw new IllegalArgumentException("stopStatsReportingForUser: Arguments cannot be null");
    }
    String key = userID + ":" + confID;
    List<ConferenceStats> tempStats = conferenceStatsMap.get(key);
    long apiTS = System.currentTimeMillis();
    if (tempStats != null && tempStats.size() > 0) {
      ConferenceStats conferenceStats = tempStats.get(0);
      ConferenceStatsEvent conferenceStatsEvent =
          new ConferenceStatsEvent(bridgeId, conferenceStats.getRemoteUserID(),
              conferenceStats.getLocalUserID(), conferenceStats.getLocalUserID(), apiTS);
      UserInfo info = new UserInfo(conferenceStats.getConfID(), conferenceStats.getRemoteUserID(),
          conferenceStats.getUcID());

      tempStats.forEach(new Consumer<ConferenceStats>() {
        public void accept(ConferenceStats stats) {
          conferenceStatsEvent.addStats(stats);
        }

      });

      String statsString = gson.toJson(conferenceStatsEvent);
      logger.fine("Stats string -" + statsString);
      sendCallStatsConferenceStats(statsString, info);
      conferenceStatsMap.remove(key);
    }
  }

  public synchronized void reportConferenceStats(String userID, ConferenceStats stats) {
    if (stats == null || userID == null) {
      logger.severe("sendConferenceStats: Arguments cannot be null ");
      throw new IllegalArgumentException("sendConferenceStats: Arguments cannot be null");
    }
    String key = userID + ":" + stats.getConfID();
    List<ConferenceStats> tempStats = conferenceStatsMap.get(key);
    if (tempStats == null) {
      // tempStats = new ArrayList<ConferenceStats>();
      throw new IllegalStateException(
          "reportConferenceStats called without calling startStatsReportingForUser");
    } else {
      tempStats.add(stats);
      conferenceStatsMap.put(key, tempStats);
    }
  }

  private synchronized void startKeepAliveThread() {
    if (bridgeKeepAliveManager == null) {
      bridgeKeepAliveManager = new CallStatsBridgeKeepAliveManager(appId, bridgeId,
          authenticator.getToken(), httpClient, new CallStatsBridgeKeepAliveStatusListener() {
            public void onKeepAliveError(CallStatsErrors error, String errMsg) {
              if (error == CallStatsErrors.AUTH_ERROR) {
                authenticator.doAuthentication();
              }
            }

            public void onSuccess() {
              sendCallStatsBridgeStatusUpdateFromQueue();
            }
          });
    }
    bridgeKeepAliveManager.startKeepAliveSender(authenticator.getToken());
  }

  private synchronized void sendCallStatsConferenceStats(String stats, UserInfo userInfo) {
    if (stats == null || userInfo == null) {
      logger.severe("sendCallStatsConferenceStats: Arguments cannot be null ");
      throw new IllegalArgumentException("sendCallStatsConferenceStats: Arguments cannot be null");
    }

    if (userInfo.getUcID() == null) {
      logger.severe("sendCallStatsConferenceStats: UCID is null ");
      throw new IllegalArgumentException("sendCallStatsConferenceStats: UCID is null");
    }

    String token = getToken();
    if (token == null) {
      logger.severe("sendCallStatsConferenceStats: Not Initialized/Token Unavaialble");
      return;
    }
    String url = "";
    try {
      url = "/" + appId + "/conferences/" + URLEncoder.encode(userInfo.getConfID(), "utf-8") + "/"
          + userInfo.getUcID() + "/stats";
    } catch (UnsupportedEncodingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    httpClient.sendBridgeStats(url, token, stats, new CallStatsHttp2ResponseListener() {
      public void onResponse(Response response) {
        int responseStatus = response.code();
        String responseString = "";
        try {
          responseString = response.body().string();
        } catch (IOException e1) {
          e1.printStackTrace();
        }
        logger.fine("sendBridgeStats : received response " + responseString);
        if (responseStatus == CallStatsResponseStatus.RESPONSE_STATUS_SUCCESS) {
          httpClient.setDisrupted(false);
        } else {

          httpClient.setDisrupted(true);
        }
      }

      public void onFailure(Exception e) {
        logger.log(Level.SEVERE, "Response exception" + e.getMessage(), e);
        httpClient.setDisrupted(true);
      }

    });

  }

} public enum CallStatsConferenceEvents {
  /** The conferenceSetup. */
  CONFERENCE_SETUP(0, "conferenceSetup"),

  /** The conferenceSetupFailed. */
  CONFERENCE_SETUP_FAILED(1, "conferenceSetupFailed"),

  /** The conferenceTerminated. */
  CONFERENCE_TERMINATED(2, "conferenceTerminated"),

  /** The userJoined. */
  USER_JOINED(3, "userJoined"),

  /** The userLeft. */
  USER_LEFT(4, "userLeft"),

  /** The fabricHold. */
  FABRIC_HOLD(5, "fabricHold"),

  /** The fabricResume. */
  FABRIC_RESUME(6, "fabricResume"),

  /** The audioMute. */
  AUDIO_MUTE(7, "audioMute"),

  /** The audioUnmute. */
  AUDIO_UNMUTE(8, "audioUnmute"),

  /** The videoPause. */
  VIDEO_PAUSE(9, "videoPause"),

  /** The videoResume. */
  VIDEO_RESUME(10, "videoResume"),

  /** The conferenceStats. */
  CONFERENCE_STATS(11, "conferenceStats");

  private final int msgCode;
  private final String msgType;

  private CallStatsConferenceEvents(int code, String msgType) {
    this.msgCode = code;
    this.msgType = msgType;
  }

  public String getMessageType() {
    return msgType;
  }

  public int getMessageCode() {
    return msgCode;
  }

  @Override
  public String toString() {
    return msgCode + ": " + msgType;
  }
} public enum CallStatsErrors {

  /** The none. */
  NONE(0, "none"),

  /** The success. */
  SUCCESS(1, "Success"),

  /** The http error. */
  HTTP_ERROR(2, "Http Error"),

  /** The auth error. */
  AUTH_ERROR(3, "Authentication Error"),

  /** The cs proto error. */
  CS_PROTO_ERROR(4, "CallStats Protocol Error"),

  /** The app connectivity error. */
  APP_CONNECTIVITY_ERROR(5, "App Connectivity Error"),

  INVALID_TOKEN_ERROR(6, "Invalid client token");

  /** The index. */
  private int index;

  /** The reason. */
  private String reason;

  /**
   * Instantiates a new call stats errors.
   *
   * @param index the index
   * @param reason the reason
   * 
   */
  private CallStatsErrors(int index, String reason) {
    this.index = index;
    this.reason = reason;
  }

  /**
   * Gets the index.
   *
   * @return the index
   */
  public int getIndex() {
    return index;
  }

  /**
   * Gets the reason.
   *
   * @return the reason
   */
  public String getReason() {
    return reason;
  }
} public interface ICallStatsTokenGenerator {
  public String generateToken(boolean forcenew);
} public class BridgeStatusInfo {

  /** measurementInterval: Interval since the last status measurement, measured in milliseconds. */
  private int measurementInterval;

  /**
   * cpuUsage: The cpu usage for the endpoint. The value is defined in
   * https://github.com/jitsi/jitsi-videobridge/blob/master/doc/using_statistics.md
   */
  private float cpuUsage;

  /**
   * memoryUsage: The memory usage for the endpoint. The value is defined in
   * https://github.com/jitsi/jitsi-videobridge/blob/master/doc/using_statistics.md
   */
  private float memoryUsage;

  /** totalMemory: The total memory available to the endpoint. */
  private float totalMemory;

  /**
   * threadCount: The number of Java threads that the bridge is using. This is optional, The value
   * is defined in https://github.com/jitsi/jitsi-videobridge/blob/master/doc/using_statistics.md
   */
  private int threadCount;

  /** intervalSentBytes: The sent bytes in the last measurement interval. */
  private long intervalSentBytes;

  /** intervalReceivedBytes: The received bytes in the last measurement interval. */
  private long intervalReceivedBytes;

  /**
   * intervalRtpFractionLoss: The packet loss observed in the last measurement interval. It is the
   * fraction of the RTP packets lost, and the RTP packets lost and received in the last measurement
   * interval. The value is between 0.0 and 1.0.
   */
  private float intervalRtpFractionLoss;

  /** totalLoss: The total cumulative packets lost. */
  private float totalLoss;

  /** avgIntervalRtt: The avg RTT observed in the last measurement interval. */
  private float avgIntervalRtt;

  /** avgIntervalJitter: The avg interval jitter in the last measurement interval. */
  private float avgIntervalJitter;

  /**
   * intervalDownloadBitRate: Download bit rate for the endpoints in kilobits per second measured
   * over the last measurement interval.
   */
  private int intervalDownloadBitRate;

  /**
   * intervalUploadBitRate: Upload bit rate for the video bridge in kilobits per second measured
   * over the last measurement interval.
   */
  private int intervalUploadBitRate;

  /**
   * The cumulative fraction loss, measured as the ratio of the cumulative RTP packets lost, and
   * cumulative sum of packets lost and received. The value is between 0.0 and 1.0.
   */
  // private float totalRtpFractionLoss;

  /** audioFabricCount: Current number of open/active 5 tuples that use audio. */
  private int audioFabricCount;

  /** videoFabricCount: Current number of open/active 5-tuples that use video. */
  private int videoFabricCount;

  /** conferenceCount: Current number of multimedia conferences. */
  private int conferenceCount;

  /**
   * participantsCount: Current number of multimedia participants cumulative over all ongoing
   * conferences.
   */
  private int participantsCount;

  /**
   * Instantiates a new bridge status info.
   *
   * @param builder the builder
   */
  public BridgeStatusInfo(BridgeStatusInfoBuilder builder) {
    super();
    this.cpuUsage = builder.getCpuUsage();
    this.memoryUsage = builder.getMemoryUsage();
    this.totalMemory = builder.getTotalMemory();
    this.threadCount = builder.getThreadCount();
    this.intervalSentBytes = builder.getIntervalSentBytes();
    this.intervalReceivedBytes = builder.getIntervalReceivedBytes();
    this.intervalRtpFractionLoss = builder.getIntervalRtpFractionLoss();
    this.totalLoss = builder.getTotalLoss();
    this.avgIntervalRtt = builder.getAvgIntervalRtt();
    this.avgIntervalJitter = builder.getAvgIntervalJitter();
    this.intervalDownloadBitRate = builder.getIntervalDownloadBitRate();
    this.intervalUploadBitRate = builder.getIntervalUploadBitRate();
    this.audioFabricCount = builder.getAudioFabricCount();
    this.videoFabricCount = builder.getVideoFabricCount();
    this.conferenceCount = builder.getConferenceCount();
    this.participantsCount = builder.getParticipantsCount();
    this.measurementInterval = builder.getMeasurementInterval();
  }


  /**
   * Gets the measurement interval.
   *
   * @return the measurement interval
   */
  public int getMeasurementInterval() {
    return measurementInterval;
  }

  /**
   * Sets the measurement interval.
   *
   * @param measurementInterval the new measurement interval
   */
  public void setMeasurementInterval(int measurementInterval) {
    this.measurementInterval = measurementInterval;
  }

  /**
   * Gets the memory usage.
   *
   * @return the memory usage
   */
  public float getMemoryUsage() {
    return memoryUsage;
  }

  /**
   * Sets the memory usage.
   *
   * @param memoryUsage the new memory usage
   */
  public void setMemoryUsage(float memoryUsage) {
    this.memoryUsage = memoryUsage;
  }

  /**
   * Gets the cpu usage.
   *
   * @return the cpu usage
   */
  public float getCpuUsage() {
    return cpuUsage;
  }

  /**
   * Sets the cpu usage.
   *
   * @param cpuUsage the new cpu usage
   */
  public void setCpuUsage(float cpuUsage) {
    this.cpuUsage = cpuUsage;
  }

  /**
   * Gets the sent bytes.
   *
   * @return the sent bytes
   */
  public long getIntervalSentBytes() {
    return intervalSentBytes;
  }

  /**
   * Sets the sent bytes.
   *
   * @param intervalSentBytes the new interval sent bytes
   */
  public void setIntervalSentBytes(long intervalSentBytes) {
    this.intervalSentBytes = intervalSentBytes;
  }

  /**
   * Gets the received bytes.
   *
   * @return the received bytes
   */
  public long getIntervalReceivedBytes() {
    return intervalReceivedBytes;
  }

  /**
   * Sets the received bytes.
   *
   * @param intervalReceivedBytes the new received bytes
   */
  public void setReceivedBytes(long intervalReceivedBytes) {
    this.intervalReceivedBytes = intervalReceivedBytes;
  }

  /**
   * Gets the interval loss.
   *
   * @return the interval loss
   */
  public float getIntervalRtpFractionLoss() {
    return intervalRtpFractionLoss;
  }

  /**
   * Sets the interval loss.
   *
   * @param intervalRtpFractionLoss the new interval rtp fraction loss
   */
  public void setIntervalRtpFractionLoss(float intervalRtpFractionLoss) {
    this.intervalRtpFractionLoss = intervalRtpFractionLoss;
  }

  /**
   * Gets the total loss.
   *
   * @return the total loss
   */
  public float getTotalLoss() {
    return totalLoss;
  }

  /**
   * Sets the total loss.
   *
   * @param totalLoss the new total loss
   */
  public void setTotalLoss(float totalLoss) {
    this.totalLoss = totalLoss;
  }

  /**
   * Gets the avg interval rtt.
   *
   * @return the avg interval rtt
   */
  public float getAvgIntervalRtt() {
    return avgIntervalRtt;
  }

  /**
   * Sets the avg interval rtt.
   *
   * @param avgIntervalRtt the new avg interval rtt
   */
  public void setAvgIntervalRtt(float avgIntervalRtt) {
    this.avgIntervalRtt = avgIntervalRtt;
  }

  /**
   * Gets the avg interval jitter.
   *
   * @return the avg interval jitter
   */
  public float getAvgIntervalJitter() {
    return avgIntervalJitter;
  }

  /**
   * Sets the avg interval jitter.
   *
   * @param avgIntervalJitter the new avg interval jitter
   */
  public void setAvgIntervalJitter(float avgIntervalJitter) {
    this.avgIntervalJitter = avgIntervalJitter;
  }

  /**
   * Gets the total memory.
   *
   * @return the total memory
   */
  public float getTotalMemory() {
    return totalMemory;
  }

  /**
   * Sets the total memory.
   *
   * @param totalMemory the new total memory
   */
  public void setTotalMemory(float totalMemory) {
    this.totalMemory = totalMemory;
  }

  /**
   * Gets the thread count.
   *
   * @return the thread count
   */
  public int getThreadCount() {
    return threadCount;
  }

  /**
   * Sets the thread count.
   *
   * @param threadCount the new thread count
   */
  public void setThreadCount(int threadCount) {
    this.threadCount = threadCount;
  }

  /**
   * Gets the download bit rate.
   *
   * @return the download bit rate
   */
  public int getIntervalDownloadBitRate() {
    return intervalDownloadBitRate;
  }

  /**
   * Sets the download bit rate.
   *
   * @param intervalDownloadBitRate the new interval download bit rate
   */
  public void setIntervalDownloadBitRate(int intervalDownloadBitRate) {
    this.intervalDownloadBitRate = intervalDownloadBitRate;
  }

  /**
   * Gets the upload bit rate.
   *
   * @return the upload bit rate
   */
  public int getIntervalUploadBitRate() {
    return intervalUploadBitRate;
  }

  /**
   * Sets the upload bit rate.
   *
   * @param intervalUploadBitRate the new interval upload bit rate
   */
  public void setIntervalUploadBitRate(int intervalUploadBitRate) {
    this.intervalUploadBitRate = intervalUploadBitRate;
  }


  /**
   * Gets the audio channels count.
   *
   * @return the audio channels count
   */
  public int getAudioFabricCount() {
    return audioFabricCount;
  }

  /**
   * Sets the audio channels count.
   *
   * @param audioFabricCount the new audio fabric count
   */
  public void setAudioFabricCount(int audioFabricCount) {
    this.audioFabricCount = audioFabricCount;
  }

  /**
   * Gets the video channels count.
   *
   * @return the video channels count
   */
  public int getVideoFabricCount() {
    return videoFabricCount;
  }

  /**
   * Sets the video channels count.
   *
   * @param videoFabricCount the new video fabric count
   */
  public void setVideoFabricCount(int videoFabricCount) {
    this.videoFabricCount = videoFabricCount;
  }

  /**
   * Gets the conference count.
   *
   * @return the conference count
   */
  public int getConferenceCount() {
    return conferenceCount;
  }

  /**
   * Sets the conference count.
   *
   * @param conferenceCount the new conference count
   */
  public void setConferenceCount(int conferenceCount) {
    this.conferenceCount = conferenceCount;
  }

  /**
   * Gets the participants count.
   *
   * @return the participants count
   */
  public int getParticipantsCount() {
    return participantsCount;
  }

  /**
   * Sets the participants count.
   *
   * @param participantsCount the new participants count
   */
  public void setParticipantsCount(int participantsCount) {
    this.participantsCount = participantsCount;
  }

} public class BridgeStatusInfoBuilder {

  /** measurementInterval: Interval since the last status measurement, measured in milliseconds. */
  private int measurementInterval;

  /**
   * cpuUsage: The cpu usage for the endpoint. The value is defined in
   * https://github.com/jitsi/jitsi-videobridge/blob/master/doc/using_statistics.md
   */
  private float cpuUsage;

  /**
   * memoryUsage: The memory usage for the endpoint. The value is defined in
   * https://github.com/jitsi/jitsi-videobridge/blob/master/doc/using_statistics.md
   */
  private float memoryUsage;

  /** totalMemory: The total memory available to the endpoint. */
  private float totalMemory;

  /**
   * threadCount: The number of Java threads that the bridge is using. This is optional, The value
   * is defined in https://github.com/jitsi/jitsi-videobridge/blob/master/doc/using_statistics.md
   */
  private int threadCount;

  /** intervalSentBytes: The sent bytes in the last measurement interval. */
  private long intervalSentBytes;

  /** intervalReceivedBytes: The received bytes in the last measurement interval. */
  private long intervalReceivedBytes;

  /**
   * intervalRtpFractionLoss: The packet loss observed in the last measurement interval. It is the
   * fraction of the RTP packets lost, and the RTP packets lost and received in the last measurement
   * interval. The value is between 0.0 and 1.0.
   */
  private float intervalRtpFractionLoss;

  /** totalLoss: The total cumulative packets lost. */
  private float totalLoss;

  /** avgIntervalRtt: The avg RTT observed in the last measurement interval. */
  private float avgIntervalRtt;

  /** avgIntervalJitter: The avg interval jitter in the last measurement interval. */
  private float avgIntervalJitter;

  /**
   * intervalDownloadBitRate: Download bit rate for the endpoints in kilobits per second measured
   * over the last measurement interval.
   */
  private int intervalDownloadBitRate;

  /**
   * intervalUploadBitRate: Upload bit rate for the video bridge in kilobits per second measured
   * over the last measurement interval.
   */
  private int intervalUploadBitRate;

  /**
   * The cumulative fraction loss, measured as the ratio of the cumulative RTP packets lost, and
   * cumulative sum of packets lost and received. The value is between 0.0 and 1.0.
   */
  // private float totalRtpFractionLoss;

  /** audioFabricCount: Current number of open/active 5 tuples that use audio. */
  private int audioFabricCount;

  /** videoFabricCount: Current number of open/active 5-tuples that use video. */
  private int videoFabricCount;

  /** conferenceCount: Current number of multimedia conferences. */
  private int conferenceCount;

  /**
   * participantsCount: Current number of multimedia participants cumulative over all ongoing
   * conferences.
   */
  private int participantsCount;

  /**
   * Gets the measurement interval.
   *
   * @return the measurement interval
   */
  public int getMeasurementInterval() {
    return measurementInterval;
  }

  /**
   * Gets the cpu usage.
   *
   * @return the cpu usage
   */
  public float getCpuUsage() {
    return cpuUsage;
  }

  /**
   * Gets the memory usage.
   *
   * @return the memory usage
   */
  public float getMemoryUsage() {
    return memoryUsage;
  }

  /**
   * Gets the total memory.
   *
   * @return the total memory
   */
  public float getTotalMemory() {
    return totalMemory;
  }

  /**
   * Gets the thread count.
   *
   * @return the thread count
   */
  public int getThreadCount() {
    return threadCount;
  }

  /**
   * Gets the interval sent bytes.
   *
   * @return the interval sent bytes
   */
  public long getIntervalSentBytes() {
    return intervalSentBytes;
  }

  /**
   * Gets the interval received bytes.
   *
   * @return the interval received bytes
   */
  public long getIntervalReceivedBytes() {
    return intervalReceivedBytes;
  }

  /**
   * Gets the interval rtp fraction loss.
   *
   * @return the interval rtp fraction loss
   */
  public float getIntervalRtpFractionLoss() {
    return intervalRtpFractionLoss;
  }

  /**
   * Gets the total loss.
   *
   * @return the total loss
   */
  public float getTotalLoss() {
    return totalLoss;
  }

  /**
   * Gets the avg interval rtt.
   *
   * @return the avg interval rtt
   */
  public float getAvgIntervalRtt() {
    return avgIntervalRtt;
  }

  /**
   * Gets the avg interval jitter.
   *
   * @return the avg interval jitter
   */
  public float getAvgIntervalJitter() {
    return avgIntervalJitter;
  }

  /**
   * Gets the interval download bit rate.
   *
   * @return the interval download bit rate
   */
  public int getIntervalDownloadBitRate() {
    return intervalDownloadBitRate;
  }

  /**
   * Gets the interval upload bit rate.
   *
   * @return the interval upload bit rate
   */
  public int getIntervalUploadBitRate() {
    return intervalUploadBitRate;
  }


  /**
   * Gets the audio fabric count.
   *
   * @return the audio fabric count
   */
  public int getAudioFabricCount() {
    return audioFabricCount;
  }

  /**
   * Gets the video fabric count.
   *
   * @return the video fabric count
   */
  public int getVideoFabricCount() {
    return videoFabricCount;
  }

  /**
   * Gets the conference count.
   *
   * @return the conference count
   */
  public int getConferenceCount() {
    return conferenceCount;
  }

  /**
   * Gets the participants count.
   *
   * @return the participants count
   */
  public int getParticipantsCount() {
    return participantsCount;
  }


  /**
   * Instantiates a new bridge status info builder.
   */
  public BridgeStatusInfoBuilder() {

  }

  /**
   * Cpu usage.
   *
   * @param cpuUsage the cpu usage
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder cpuUsage(float cpuUsage) {
    this.cpuUsage = cpuUsage;
    return this;
  }

  /**
   * Memory usage.
   *
   * @param memoryUsage the memory usage
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder memoryUsage(float memoryUsage) {
    this.memoryUsage = memoryUsage;
    return this;
  }

  /**
   * Total memory.
   *
   * @param totalMemory the total memory
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder totalMemory(float totalMemory) {
    this.totalMemory = totalMemory;
    return this;
  }

  /**
   * Thread count.
   *
   * @param threadCount the thread count
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder threadCount(int threadCount) {
    this.threadCount = threadCount;
    return this;
  }


  /**
   * Sent bytes.
   *
   * @param intervalSentBytes the interval sent bytes
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder intervalSentBytes(long intervalSentBytes) {
    this.intervalSentBytes = intervalSentBytes;
    return this;
  }

  /**
   * Received bytes.
   *
   * @param intervalReceivedBytes the interval received bytes
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder intervalReceivedBytes(long intervalReceivedBytes) {
    this.intervalReceivedBytes = intervalReceivedBytes;
    return this;
  }

  /**
   * Interval loss.
   *
   * @param intervalRtpFractionLoss the interval rtp fraction loss
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder intervalRtpFractionLoss(float intervalRtpFractionLoss) {
    this.intervalRtpFractionLoss = intervalRtpFractionLoss;
    return this;
  }

  /**
   * Total loss.
   *
   * @param totalLoss the total loss
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder totalLoss(float totalLoss) {
    this.totalLoss = totalLoss;
    return this;
  }

  /**
   * Avg interval rtt.
   *
   * @param avgIntervalRtt the avg interval rtt
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder avgIntervalRtt(float avgIntervalRtt) {
    this.avgIntervalRtt = avgIntervalRtt;
    return this;
  }

  /**
   * Avg interval jitter.
   *
   * @param avgIntervalJitter the avg interval jitter
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder avgIntervalJitter(float avgIntervalJitter) {
    this.avgIntervalJitter = avgIntervalJitter;
    return this;
  }

  /**
   * Interval download bit rate.
   *
   * @param intervalDownloadBitRate the interval download bit rate
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder intervalDownloadBitRate(int intervalDownloadBitRate) {
    this.intervalDownloadBitRate = intervalDownloadBitRate;
    return this;
  }

  /**
   * Interval upload bit rate.
   *
   * @param intervalUploadBitRate the interval upload bit rate
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder intervalUploadBitRate(int intervalUploadBitRate) {
    this.intervalUploadBitRate = intervalUploadBitRate;
    return this;
  }

  /**
   * Audio fabric count.
   *
   * @param audioFabricCount the audio fabric count
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder audioFabricCount(int audioFabricCount) {
    this.audioFabricCount = audioFabricCount;
    return this;
  }

  /**
   * Video fabric count.
   *
   * @param videoFabricCount the video fabric count
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder videoFabricCount(int videoFabricCount) {
    this.videoFabricCount = videoFabricCount;
    return this;
  }

  /**
   * Conference count.
   *
   * @param conferenceCount the conference count
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder conferenceCount(int conferenceCount) {
    this.conferenceCount = conferenceCount;
    return this;
  }

  /**
   * Participants count.
   *
   * @param participantsCount the participants count
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder participantsCount(int participantsCount) {
    this.participantsCount = participantsCount;
    return this;
  }

  /**
   * Measurement interval.
   *
   * @param measurementInterval the measurement interval
   * @return the bridge status info builder
   */
  public BridgeStatusInfoBuilder measurementInterval(int measurementInterval) {
    this.measurementInterval = measurementInterval;
    return this;
  }

  /**
   * Builds the.
   *
   * @return the bridge status info
   */
  public BridgeStatusInfo build() {
    return new BridgeStatusInfo(this);
  }
} public enum CallStatsStreamType {

  INBOUND(0, "inbound"), OUTBOUND(1, "outbound");

  private final int typeCode;
  private final String type;

  private CallStatsStreamType(int code, String msgType) {
    this.typeCode = code;
    this.type = msgType;
  }

  public String getMessageType() {
    return type;
  }

  public int getMessageCode() {
    return typeCode;
  }

  @Override
  public String toString() {
    return type + ": " + type;
  }
} public class ConferenceInfo {

  private String confID;
  private String initiatorID;
  private String initiatorSiteID;

  public String getConfID() {
    return confID;
  }

  public void setConfID(String confID) {
    this.confID = confID;
  }

  public String getInitiatorID() {
    return initiatorID;
  }

  public void setInitiatorID(String initiatorID) {
    this.initiatorID = initiatorID;
  }

  public String getInitiatorSiteID() {
    return initiatorSiteID;
  }

  public void setInitiatorSiteID(String initiatorSiteID) {
    this.initiatorSiteID = initiatorSiteID;
  }

  public ConferenceInfo(String confID, String initiatorID) {
    this(confID, initiatorID, "");
  }
  public ConferenceInfo(String confID, String initiatorID, String initiatorSiteID) {
    super();
    this.confID = confID;
    this.initiatorID = initiatorID;
    this.initiatorSiteID = initiatorSiteID;
  }
} public class ConferenceStats {

  transient String localID;
  transient String remoteID;
  @SerializedName("type")
  String statsType; // inbound or outbound
  long ssrc;
  String originID;
  transient String ucID;
  transient String confID;
  Long packetsSent;
  Long packetsReceived;
  Long packetsLost;
  Long packetsDuplicated;
  Long packetsDiscarded;
  Long packetsRepaired;

  Long bytesSent;
  Long bytesReceived;
  Long bytesDuplicated;
  Long bytesDiscarded;
  Long bytesRepaired;

  Long burstPacketsLost;
  Long burstLossIntervalCount;
  Long burstPacketsDiscarded;
  Long burstDiscardIntervalCount;

  Double gapLossRate;
  Double gapDiscardRate;

  Double fractionalPacketLost;
  Double fractionalPacketDiscarded;

  Long framesSent;
  Long framesReceived;
  Long framesLost;
  Long framesDropped;
  Long framesCorrupted;

  Integer rtt;
  Double jitter;

  Double currentPlayoutDelay;
  Double maxPlayoutDelay;
  Double minPlayoutDelay;

  Double currentJBDelay;
  Double highWatermarkJBDelay;
  Double lowWatermarkJBDelay;
  Double maxJBDelay;
  Double minJBDelay;
  Double avsync;
  String mediaType;

  public ConferenceStats() {

  }

  public ConferenceStats(ConferenceStatsBuilder builder) {
    this.localID = builder.getLocalUserID();
    this.statsType = builder.getStatsType();
    this.originID = builder.getFromUserID();
    this.ssrc = Long.parseLong(builder.getSsrc());
    this.remoteID = builder.getRemoteUserID();
    this.ucID = builder.getUcID();
    this.confID = builder.getConfID();
    this.packetsSent = builder.getPacketsSent();
    this.packetsReceived = builder.getPacketsReceived();
    this.packetsLost = builder.getPacketsLost();
    this.packetsDuplicated = builder.getPacketsDuplicated();
    this.packetsDiscarded = builder.getPacketsDiscarded();
    this.packetsRepaired = builder.getPacketsRepaired();

    this.bytesSent = builder.getBytesSent();
    this.bytesReceived = builder.getBytesReceived();
    this.bytesDuplicated = builder.getBytesDuplicated();
    this.bytesDiscarded = builder.getBytesDiscarded();
    this.bytesRepaired = builder.getBytesRepaired();

    this.burstPacketsLost = builder.getBurstPacketsLost();
    this.burstLossIntervalCount = builder.getBurstLossIntervalCount();
    this.burstPacketsDiscarded = builder.getBurstPacketsDiscarded();
    this.burstDiscardIntervalCount = builder.getBurstDiscardIntervalCount();

    this.gapLossRate = builder.getGapLossRate();
    this.gapDiscardRate = builder.getGapDiscardRate();

    this.fractionalPacketLost = builder.getFractionalPacketLost();
    this.fractionalPacketDiscarded = builder.getFractionalPacketDiscarded();

    this.framesSent = builder.getFramesSent();
    this.framesReceived = builder.getFramesReceived();
    this.framesLost = builder.getFramesLost();
    this.framesDropped = builder.getFramesDropped();
    this.framesCorrupted = builder.getFramesCorrupted();

    this.rtt = builder.getRtt();
    this.jitter = builder.getJitter();

    this.currentPlayoutDelay = builder.getCurrentPlayoutDelay();
    this.maxPlayoutDelay = builder.getMaxPlayoutDelay();
    this.minPlayoutDelay = builder.getMinPlayoutDelay();

    this.currentJBDelay = builder.getCurrentJBDelay();
    this.highWatermarkJBDelay = builder.getHighWatermarkJBDelay();
    this.lowWatermarkJBDelay = builder.getLowWatermarkJBDelay();
    this.maxJBDelay = builder.getMaxJBDelay();
    this.minJBDelay = builder.getMinJBDelay();
    this.avsync = builder.getAvsync();
    this.mediaType = builder.getMediaType();
  }

  public String getUcID() {
    return this.ucID;
  }

  public void setUcID(String ucID) {
    this.ucID = ucID;
  }

  public String getConfID() {
    return this.confID;
  }

  public void setConfID(String confID) {
    this.confID = confID;
  }

  public String getLocalUserID() {
    return this.localID;
  }

  public void setLocalUserID(String localUserID) {
    this.localID = localUserID;
  }

  public long getSsrc() {
    return this.ssrc;
  }

  public void setSsrc(String ssrc) {
    this.ssrc = Long.parseLong(ssrc);
  }

  public String getFromUserID() {
    return this.originID;
  }

  public void setFromUserID(String fromUserID) {
    this.originID = fromUserID;
  }

  public String getStatsType() {
    return this.statsType;
  }

  public void setStatsType(CallStatsStreamType statsType) {
    this.statsType = statsType.getMessageType();
  }

  public String getRemoteUserID() {
    return this.remoteID;
  }

  public void setRemoteUserID(String remoteUserID) {
    this.remoteID = remoteUserID;
  }
  
  public String getMediaType() {
	return this.mediaType;
  }

  public void setMediaType(String mediaType) {
    this.mediaType = mediaType;
  }
} public class ConferenceStatsBuilder {

  String localUserID;
  String remoteUserID;
  String statsType; // inbound or outbound
  String ssrc;
  String fromUserID;
  String ucID;
  String confID;
  String mediaType;

  Long packetsReceived;
  Long packetsLost;
  Long packetsDuplicated;
  Long packetsDiscarded;
  Long packetsRepaired;

  Long bytesSent;
  Long bytesReceived;
  Long bytesDuplicated;
  Long bytesDiscarded;
  Long bytesRepaired;

  Long burstPacketsLost;
  Long burstLossIntervalCount;
  Long burstPacketsDiscarded;
  Long burstDiscardIntervalCount;

  Double gapLossRate;
  Double gapDiscardRate;

  Double fractionalPacketLost;
  Double fractionalPacketDiscarded;

  Long framesSent;
  Long framesReceived;
  Long framesLost;
  Long framesDropped;
  Long framesCorrupted;

  Integer rtt;
  Double jitter;

  Double currentPlayoutDelay;
  Double maxPlayoutDelay;
  Double minPlayoutDelay;

  Double currentJBDelay;
  Double highWatermarkJBDelay;
  Double lowWatermarkJBDelay;
  Double maxJBDelay;
  Double minJBDelay;
  Double avsync;

  Long packetsSent;

  public Long getPacketsReceived() {
    return packetsReceived;
  }

  public Long getPacketsLost() {
    return packetsLost;
  }

  public Long getPacketsDuplicated() {
    return packetsDuplicated;
  }

  public Long getPacketsDiscarded() {
    return packetsDiscarded;
  }

  public Long getPacketsRepaired() {
    return packetsRepaired;
  }

  public Long getBytesReceived() {
    return bytesReceived;
  }

  public Long getBytesDuplicated() {
    return bytesDuplicated;
  }

  public Long getBytesDiscarded() {
    return bytesDiscarded;
  }

  public Long getBytesRepaired() {
    return bytesRepaired;
  }

  public Long getBurstPacketsLost() {
    return burstPacketsLost;
  }

  public Long getBurstLossIntervalCount() {
    return burstLossIntervalCount;
  }

  public Long getBurstPacketsDiscarded() {
    return burstPacketsDiscarded;
  }

  public Long getBurstDiscardIntervalCount() {
    return burstDiscardIntervalCount;
  }

  public Double getGapLossRate() {
    return gapLossRate;
  }

  public Double getGapDiscardRate() {
    return gapDiscardRate;
  }

  public Double getFractionalPacketLost() {
    return fractionalPacketLost;
  }

  public Double getFractionalPacketDiscarded() {
    return fractionalPacketDiscarded;
  }

  public Long getFramesSent() {
    return framesSent;
  }

  public Long getFramesReceived() {
    return framesReceived;
  }

  public Long getFramesLost() {
    return framesLost;
  }

  public Long getFramesDropped() {
    return framesDropped;
  }

  public Long getFramesCorrupted() {
    return framesCorrupted;
  }

  public Double getCurrentPlayoutDelay() {
    return currentPlayoutDelay;
  }

  public Double getMaxPlayoutDelay() {
    return maxPlayoutDelay;
  }

  public Double getMinPlayoutDelay() {
    return minPlayoutDelay;
  }

  public Double getCurrentJBDelay() {
    return currentJBDelay;
  }

  public Double getHighWatermarkJBDelay() {
    return highWatermarkJBDelay;
  }

  public Double getLowWatermarkJBDelay() {
    return lowWatermarkJBDelay;
  }

  public Double getMaxJBDelay() {
    return maxJBDelay;
  }

  public Double getMinJBDelay() {
    return minJBDelay;
  }

  public Double getAvsync() {
    return avsync;
  }

  public String getLocalUserID() {
    return localUserID;
  }

  public String getStatsType() {
    return statsType;
  }

  public String getSsrc() {
    return ssrc;
  }

  public String getFromUserID() {
    return fromUserID;
  }

  public Integer getRtt() {
    return rtt;
  }

  public Long getPacketsSent() {
    return packetsSent;
  }

  public Long getBytesSent() {
    return bytesSent;
  }

  public Double getJitter() {
    return jitter;
  }

  public String getUcID() {
    return ucID;
  }

  public String getConfID() {
    return confID;
  }

  public String getRemoteUserID() {
    return remoteUserID;
  }
  
  public String getMediaType() {
	return mediaType;
  }
  
  
  public ConferenceStatsBuilder localUserID(String localUserID) {
    this.localUserID = localUserID;
    return this;
  }

  public ConferenceStatsBuilder statsType(CallStatsStreamType statsType) {
    this.statsType = statsType.getMessageType();
    return this;
  }
  
  public ConferenceStatsBuilder mediaType(String mediaType) {	
	this.mediaType = mediaType;
	return this;
  }

  public ConferenceStatsBuilder ssrc(String ssrc) {
    this.ssrc = ssrc;
    return this;
  }

  public ConferenceStatsBuilder fromUserID(String fromUserID) {
    this.fromUserID = fromUserID;
    return this;
  }

  public ConferenceStatsBuilder remoteUserID(String remoteUserID) {
    this.remoteUserID = remoteUserID;
    return this;
  }

  public ConferenceStatsBuilder ucID(String ucID) {
    this.ucID = ucID;
    return this;
  }

  public ConferenceStatsBuilder confID(String confID) {
    this.confID = confID;
    return this;
  }


  public ConferenceStatsBuilder packetsSent(long packetsSent) {
    this.packetsSent = packetsSent;
    return this;
  }

  public ConferenceStatsBuilder packetsReceived(long packetsReceived) {
    this.packetsReceived = packetsReceived;
    return this;
  }

  public ConferenceStatsBuilder packetsLost(long packetsLost) {
    this.packetsLost = packetsLost;
    return this;
  }

  public ConferenceStatsBuilder packetsDuplicated(long packetsDuplicated) {
    this.packetsDuplicated = packetsDuplicated;
    return this;
  }

  public ConferenceStatsBuilder packetsDiscarded(long packetsDiscarded) {
    this.packetsDiscarded = packetsDiscarded;
    return this;
  }

  public ConferenceStatsBuilder packetsRepaired(long packetsRepaired) {
    this.packetsRepaired = packetsRepaired;
    return this;
  }

  public ConferenceStatsBuilder bytesSent(long bytesSent) {
    this.bytesSent = bytesSent;
    return this;
  }

  public ConferenceStatsBuilder bytesReceived(long bytesReceived) {
    this.bytesReceived = bytesReceived;
    return this;
  }

  public ConferenceStatsBuilder bytesDuplicated(long bytesDuplicated) {
    this.bytesDuplicated = bytesDuplicated;
    return this;
  }

  public ConferenceStatsBuilder bytesDiscarded(long bytesDiscarded) {
    this.bytesDiscarded = bytesDiscarded;
    return this;
  }

  public ConferenceStatsBuilder bytesRepaired(long bytesRepaired) {
    this.bytesRepaired = bytesRepaired;
    return this;
  }

  public ConferenceStatsBuilder burstPacketsLost(long burstPacketsLost) {
    this.burstPacketsLost = burstPacketsLost;
    return this;
  }

  public ConferenceStatsBuilder burstLossIntervalCount(long burstLossIntervalCount) {
    this.burstLossIntervalCount = burstLossIntervalCount;
    return this;
  }

  public ConferenceStatsBuilder burstPacketsDiscarded(long burstPacketsDiscarded) {
    this.burstPacketsDiscarded = burstPacketsDiscarded;
    return this;
  }

  public ConferenceStatsBuilder burstDiscardIntervalCount(long burstDiscardIntervalCount) {
    this.burstDiscardIntervalCount = burstDiscardIntervalCount;
    return this;
  }

  public ConferenceStatsBuilder gapLossRate(double gapLossRate) {
    this.gapLossRate = gapLossRate;
    return this;
  }

  public ConferenceStatsBuilder gapDiscardRate(double gapDiscardRate) {
    this.gapDiscardRate = gapDiscardRate;
    return this;
  }

  public ConferenceStatsBuilder fractionalPacketLost(double fractionalPacketLost) {
    this.fractionalPacketLost = fractionalPacketLost;
    return this;
  }

  public ConferenceStatsBuilder fractionalPacketDiscarded(double fractionalPacketDiscarded) {
    this.fractionalPacketDiscarded = fractionalPacketDiscarded;
    return this;
  }

  public ConferenceStatsBuilder framesSent(long framesSent) {
    this.framesSent = framesSent;
    return this;
  }

  public ConferenceStatsBuilder framesReceived(long framesReceived) {
    this.framesReceived = framesReceived;
    return this;
  }

  public ConferenceStatsBuilder framesLost(long framesLost) {
    this.framesLost = framesLost;
    return this;
  }

  public ConferenceStatsBuilder framesDropped(long framesDropped) {
    this.framesDropped = framesDropped;
    return this;
  }

  public ConferenceStatsBuilder framesCorrupted(long framesCorrupted) {
    this.framesCorrupted = framesCorrupted;
    return this;
  }

  public ConferenceStatsBuilder rtt(int rtt) {
    this.rtt = rtt;
    return this;
  }

  public ConferenceStatsBuilder jitter(double jitter) {
    this.jitter = jitter;
    return this;
  }

  public ConferenceStatsBuilder currentPlayoutDelay(double currentPlayoutDelay) {
    this.currentPlayoutDelay = currentPlayoutDelay;
    return this;
  }

  public ConferenceStatsBuilder maxPlayoutDelay(double maxPlayoutDelay) {
    this.maxPlayoutDelay = maxPlayoutDelay;
    return this;
  }

  public ConferenceStatsBuilder minPlayoutDelay(double minPlayoutDelay) {
    this.minPlayoutDelay = minPlayoutDelay;
    return this;
  }

  public ConferenceStatsBuilder currentJBDelay(double currentJBDelay) {
    this.currentJBDelay = currentJBDelay;
    return this;
  }

  public ConferenceStatsBuilder highWatermarkJBDelay(double highWatermarkJBDelay) {
    this.highWatermarkJBDelay = highWatermarkJBDelay;
    return this;
  }

  public ConferenceStatsBuilder lowWatermarkJBDelay(double lowWatermarkJBDelay) {
    this.lowWatermarkJBDelay = lowWatermarkJBDelay;
    return this;
  }

  public ConferenceStatsBuilder maxJBDelay(double maxJBDelay) {
    this.maxJBDelay = maxJBDelay;
    return this;
  }

  public ConferenceStatsBuilder minJBDelay(double minJBDelay) {
    this.minJBDelay = minJBDelay;
    return this;
  }

  public ConferenceStatsBuilder avsync(double avsync) {
    this.avsync = avsync;
    return this;
  }

  public ConferenceStats build() {
    return new ConferenceStats(this);
  }
} public class ServerInfo {

  /** The name. */
  String buildName;

  String buildVersion;
  /** The os. */
  String os;

  /** The ver. */
  String osVersion;

  String appVersion;

  String callstatsVersion;

  String type = CallStatsConst.END_POINT_TYPE;

  /**
   * Instantiates a new ServerInfo info.
   */
  public ServerInfo() {
    callstatsVersion = CallStatsConst.CS_VERSION;
  }

  /**
   * Gets the name.
   *
   * @return the name
   */
  public String getName() {
    return buildName;
  }

  /**
   * Sets the name.
   *
   * @param name the new name
   */
  public void setName(String name) {
    this.buildName = name;
  }

  /**
   * Gets the os.
   *
   * @return the os
   */
  public String getOs() {
    return os;
  }

  /**
   * Sets the os.
   *
   * @param os the new os
   */
  public void setOs(String os) {
    this.os = os;
  }

  /**
   * Gets the ver.
   *
   * @return the ver
   */
  public String getVer() {
    return osVersion;
  }

  /**
   * Sets the ver.
   *
   * @param ver the new ver
   */
  public void setVer(String ver) {
    this.osVersion = ver;
  }

  public String getEndpointType() {
    return type;
  }

  public void setEndpointType(String endpointType) {
    this.type = endpointType;
  }

} public class UserInfo {

  private String confID;
  private String userID;
  private String ucID;
  private String connectionID;

  public String getConnectionID() {
    return connectionID;
  }

  public void setConnectionID(String connectionID) {
    this.connectionID = connectionID;
  }

  public String getConfID() {
    return confID;
  }

  public void setConfID(String confID) {
    this.confID = confID;
  }

  public String getUserID() {
    return userID;
  }

  public void setUserID(String userID) {
    this.userID = userID;
  }

  public String getUcID() {
    return ucID;
  }

  public void setUcID(String ucID) {
    this.ucID = ucID;
  }

  public UserInfo(String confID, String userID, String ucID) {
    super();
    this.confID = confID;
    this.userID = userID;
    this.ucID = ucID;
    this.connectionID = ucID;
  }

  public UserInfo(String confID, String userID, String ucID, String connectionID) {
    super();
    this.confID = confID;
    this.userID = userID;
    this.ucID = ucID;
    this.connectionID = connectionID;
  }

} public class CallStatsConst {

  /** The Constant CallStatsJavaSDKPropertyFileName. */
  public static String CallStatsJavaSDKPropertyFileName = "config/callstats-java-sdk.properties";

  /** The Constant CS_VERSION. */
  public static String CS_VERSION;

  /** The Constant END_POINT_TYPE. */
  public static final String END_POINT_TYPE = "middlebox";

  public static final String AUTH_TYPE = "BasicAuth";

  /** The Constant bridgeEventUrl. */
  public static final String bridgeEventUrl = "/callStatsBridgeEvent";

  public static final String conferenceEventUrl = "/callStatsBridgeConfereceEvents";

  /** The Constant httpPostMethod. */
  public static final String httpPostMethod = "POST";

  public static final String ERROR = "Error";

  public static final String SUCCESS = "OK";

  public static final String INVALID_TOKEN = "Invalid client token";

  public static final int CONNECTION_TIMEOUT = 30000;

  public static final int SO_TIMEOUT = 30000;

  public static final int KEEPALIVE_INTERVAL = 30000;

  public static final String STATS_CLIENT_URL = "CallStats.BaseURL";

  public static final String AUTH_CLIENT_URL = "CallStats.BaseURL";

} public interface CallStatsInitListener {

  /**
   * On intilization error.
   *
   * @param error the error
   * @param errMsg the err msg
   */
  void onError(CallStatsErrors error, String errMsg);

  /**
   * On initialized.
   *
   * @param msg the msg
   */
  void onInitialized(String msg);
} public interface CallStatsStartConferenceListener {

  public void onResponse(String ucid);

  void onError(CallStatsErrors error, String errMsg);

}
import io.callstats.sdk.CallStats;
import io.callstats.sdk.CallStatsConferenceEvents;
import io.callstats.sdk.CallStatsErrors;
import io.callstats.sdk.ICallStatsTokenGenerator;
import io.callstats.sdk.data.BridgeStatusInfo;
import io.callstats.sdk.data.BridgeStatusInfoBuilder;
import io.callstats.sdk.data.CallStatsStreamType;
import io.callstats.sdk.data.ConferenceInfo;
import io.callstats.sdk.data.ConferenceStats;
import io.callstats.sdk.data.ConferenceStatsBuilder;
import io.callstats.sdk.data.ServerInfo;
import io.callstats.sdk.data.UserInfo;
import io.callstats.sdk.internal.CallStatsConst;
import io.callstats.sdk.listeners.CallStatsInitListener;
import io.callstats.sdk.listeners.CallStatsStartConferenceListener;
import java.util.Random;
package io.callstats.sample.sdk;



public class App {

	// CallStats Library class
	private static CallStats callStatslib;

	private static int appId = 476165271;
	private static String keyId = "8a780f897771199253";
	private static String appSecret = "appSecret";
	//lUdrzU0L97cM
	private static String bridgeId = "bridgeId";
	// Use generated token rather than app secret
	private static boolean tokenauth = true;
	private static String ECDSAPrivateKeyPath = "/tmp/key2.pem";

	public static void main(String[] args) {

		// EndpointInfo sets the information related to the endpoint such as
		// name, os, version etc.
		ServerInfo endpointInfo = new ServerInfo();

		endpointInfo.setName("Name");
		endpointInfo.setOs("LINUX");
		endpointInfo.setVer("4.4");

		// Instantiate the callStats object
		callStatslib = new CallStats();
		CallStatsConst.CS_VERSION = "3.12.0";
		
		CallStatsInitListener callback = new CallStatsInitListener() {

			public void onInitialized(String msg) {
				// TODO Auto-generated method stub
				System.out.println("Initialized " + msg);
				// After the library is initialized, video bridge can start sending
				// the status information.
				sendBridgeStatusInfoMessage();
			}

			public void onError(CallStatsErrors error, String errMsg) {
				// TODO Auto-generated method stub
				System.out.println("Initialization Error " + errMsg);
			}
		};
		// Initialize the callStats library using appId, appSecret/token generator and listener
		if (tokenauth) {
			ICallStatsTokenGenerator tokenGenerator = new LocalTokenGenerator(appId, keyId, bridgeId, ECDSAPrivateKeyPath);
			callStatslib.initialize(appId, tokenGenerator, bridgeId, endpointInfo, callback);
		} else
			callStatslib.initialize(appId, appSecret, bridgeId, endpointInfo, callback);

	}

	public static void sendBridgeStatusInfoMessage() {
		// Building the video bridge status info.
		Random randomGenerator = new Random();
		BridgeStatusInfoBuilder bridgeStatusInfoBuilder = new BridgeStatusInfoBuilder();
		BridgeStatusInfo bridgeStatusInfo = bridgeStatusInfoBuilder.avgIntervalJitter(randomGenerator.nextFloat())
				.cpuUsage(randomGenerator.nextInt(100)).intervalRtpFractionLoss(randomGenerator.nextFloat()).build();
		System.out.println("Sending bridge status info ");

		// Sending the video bridge status info.
		callStatslib.sendCallStatsBridgeStatusUpdate(bridgeStatusInfo);
	}

	public static void sendBridgeConferenceEvents() {
		String confID = "winter_conference";
		String intiatorID = "2345";

		ConferenceInfo conferenceInfo = new ConferenceInfo(confID, intiatorID);

		// The first event for a conference to be sent should be
		// CONFERENCE_SETUP, the conferencelistener will give you back the ucid,
		// this ucid has to used in the further conference events and conference
		// stats submission
		callStatslib.sendCallStatsConferenceEvent(CallStatsConferenceEvents.CONFERENCE_SETUP, conferenceInfo,
				new CallStatsStartConferenceListener() {

					public void onResponse(String ucid) {
						sendConferenceStats(ucid);
					}

					public void onError(CallStatsErrors error, String errMsg) {
						// TODO Auto-generated method stub

					}
				});
	}

	public static void sendConferenceStats(String ucid) {
		String userID = "2345";
		String confID = "jackk";
		System.out.println("UCID is " + ucid);
		UserInfo userInfo = new UserInfo(confID, userID, ucid);