# src/index.js

    'use strict';

    const axios = require('axios');
    const axiosExtensions = require('axios-extensions');
    const url = require('url');
    const qs = require('qs');

    /**
     * TuneIn class, the main class implementing the module
     *
     * @example
     * let tuneinOptions = {
     *  protocol: 'https',                        // Protocol to use, either http or https
     *  cacheRequests: true,                      // Wheter to cache results or not, default false
     *  cacheTTL: 1000 * 60 * 60,                 // Cache TTL, default 5 minutes
     *  partnerId: process.env.TUNEIN_PARTNERID,  // PartnerID to be used when interacting with the TuneIn API
     *  };
     *
     * let tunein = new TuneIn(tuneinOptions);
     */
    module.exports =  class TuneIn {
      /**
       * Constructur for a TuneIn object.
       * @param {object} options this is an options object with all the optionals parameters used to initialise the TuneIn client
       * @example
       * let tuneinOptions = {
       *   protocol: 'https',                        // Protocol to use, either http or https
       *   cacheRequests: true,                      // Wheter to cache results or not, default false
       *   cacheTTL: 1000 * 60 * 60,                 // Cache TTL, default 5 minutes
       *   partnerId: process.env.TUNEIN_PARTNERID,  // PartnerID to be used when interacting with the TuneIn API
       * };
       */
      constructor(options) {
        options = options || {};
        let protocol = options.protocol || 'https';
        let cacheRequests = options.cacheRequests || false;
        let cacheTTL = options.cacheTTL || 1000 * 60 * 10;
        let partnerId = options.partnerId || '';

        axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
        axios.defaults.baseURL = protocol + '://opml.radiotime.com';
        axios.defaults.params = {
          render: 'json'
        };

        if (cacheRequests === true) {
          axios.defaults.adapter = axiosExtensions.cacheAdapterEnhancer(
            axios.defaults.adapter,
            true,
            'cache',
            cacheTTL
          );
        }

        if (partnerId != '') {
          axios.defaults.params.partnerId = partnerId;
        }
      }

      /**
       * Search TuneIn for a match
       * @param {string} query a query string to search for in the TuneIn catalog
       * @example
       * let queryString = 'rai radio';
       * let search = tunein.search(queryString);
       * search.then(function(results) {
       *   console.log(results);
       * });
       */
      search(query) {
        let req = {};
        req.params = {};

        req.url = '/Search.ashx';
        req.params.query = query;

        return this._call_tunein_get(req);
      }

      /**
       * @private
       */
      _call_tunein_get(req) {
        return new Promise( (resolve, reject) => {
          axios.get(req.url, req)
            .then(function(results) {
              let head = results.data.head;
              if (head.status != 200) {
                reject(results.data);
                return new Error(`TuneIn Request error: ${head.fault}`);
              }

              for (var i in results.data.body) {
                if (results.data.body[i].URL) {
                  results.data.body[i].URLObj = url.parse(results.data.body[i].URL, true);
                }
              }
              return resolve((results.data));
            })
            .catch(function(err) {
                return new Error(`TuneIn Request error: ${err}`);
            });
        });
      }

      /**
       * @private
       */
      _call_tunein_post(req, postData) {
        return new Promise( (resolve, reject) => {
          axios.post(req.url, qs.stringify(postData), req)
            .then(function(results) {
              let head = results.data.head;
              if (head.status != 200) {
                reject(results.data);
                return new Error(`TuneIn Request error: ${head.fault}`);
              }
              return resolve((results.data));
            })
            .catch(function(err) {
                return new Error(`TuneIn Request error: ${err}`);
            });
        });
      }

      /**
       * Tune a stream. This will return an object containing details for the specific stream, including URL and format.
       * @param {string} id The ID of the stream to tune, usually resulting from a browsing or a search call
       */
      tune_radio(id) {
        let req = {};
        req.params = {};

        req.url = '/Tune.ashx';
        req.params.id = id;

        return this._call_tunein_get(req);
      }

      /**
       * Describe a stream. This method will retrieve detailed information about a stream.
       *
       * @param {string} id The ID of the stream to fetch information for
       * @param {boolean} nowplaying If true it also fetches information about the show being currently aired
       */
      describe(id, nowplaying = false) {
        let req = {};
        req.params = {};

        req.url = '/Describe.ashx';
        if (nowplaying == true) {
          req.params.c = 'nowplaying';
        }
        req.params.id = id;

        return this._call_tunein_get(req);
      }

      /**
       * Authenticate with supplied username / password, to get access to favorite and customised results
       * @param {object} options Options to be passed to the POST call, must contain user's credential
       * @example
       * let authOptions = {
       *   username: 'user@example.com',
       *   password: 'secretpassword'
       * };
       * let auth = tunein.authenticate(authOptions);
       * auth.then(function(results) {
       *   console.log(results[0].AccountId);
       * });
       */
      authenticate(options) {
        options = options || {};

        let req = {};
        req.url = '/Account.ashx';
        req.params = {};
        req.params.c = 'auth';

        return this._call_tunein_post(req, options);
      }

      /**
       * @private
       */
      _call_browse(options, req) {
        options = options || {};
        let c = options.c || '';
        let id = options.id || '';
        let filter = options.filter || '';
        let offset = options.offset || '';
        let pivot = options.pivot || '';
        let username = options.username || '';

        if (c) {
          req.params.c = c;
        }
        if (id) {
          req.params.id = id;
        }
        if (filter) {
          req.params.filter = filter;
        }
        if (offset) {
          req.params.offset = offset;
        }
        if (pivot) {
          req.params.pivot = pivot;
        }
        if (username) {
          req.params.username = username;
        }

        return this._call_tunein_get(req);
      }

      /**
       * Browse a podcast (Show) for episodes details
       * @param {object} options - Options object including the show ID
       * @example
       * let showOptions = {
       *   c: 'pbrowse',
       *   id: 'p191418'
       * };
       * let show = tunein.browse_show(showOptions);
       * show.then(function(results) {
       *   console.log(results);
       * });
       */
      browse_show(options) {
        let req = {};
        req.params = {};
        req.url = '/Tune.ashx';

        return this._call_browse(options, req);
      }

      /**
       * Browse will browse the TuneIn directory depending on the supplied options paramethers
       *
       * @param {object} options - Options object for browsing
       * @return {object} results - A JSON object with results from the remote API
       */
      browse(options) {
        let req = {};
        req.params = {};
        req.url = '/Browse.ashx';

        return this._call_browse(options, req);
      }

      /**
       * Browse the local streams category
       * @param {string} username - Currently ignored
       */
      browse_local(username) {
        return this.browse({c: 'local', username: username});
      }

      /**
       * Browse the Music streams category
       * @param {string} username - Currently ignored
       */
      browse_music(username) {
        return this.browse({c: 'music'});
      }

      /**
       * Browse the talk streams category
       * @param {string} username - Currently ignored
       */
      browse_talk(username) {
        return this.browse({c: 'talk'});
      }

      /**
       * Browse the sports streams category
       * @param {string} username - Currently ignored
       */
      browse_sports(username) {
        return this.browse({c: 'sports'});
      }

      /**
       * Browse streams locations
       * @param {string} username - Currently ignored
       */
      browse_locations(username) {
        return this.browse({id : 'r0'});
      }

      /**
       * Browse streams languages
       * @param {string} username - Currently ignored
       */
      browse_langs(username) {
        return this.browse({c: 'lang'});
      }

      /**
       * Browse the podcasts directory
       * @param {string} username - Currently ignored
       */
      browse_podcast(username) {
        return this.browse({c: 'podcast'});
      }

      /**
       * Browse the popular streams category
       * @param {string} username - Currently ignored
       */
      browse_popular(username) {
        return this.browse({c: 'popular'});
      }

      /**
       * Browse the best streams category
       * @param {string} username - Currently ignored
       */
      browse_best(username) {
        return this.browse({c: 'best'});
      }
    }