from __future__ import absolute_import
import requests
import h5py
import os
import numpy
try:
    from io import BytesIO as StringIO
except ImportError:
    from io import BytesIO as StringIO
import zlib
import tempfile
import blosc

from .Remote import Remote
from .errors import *
import ndio.ramon as ramon
from six.moves import range
import six

try:
    import urllib.request as urllib2
except ImportError:
    import urllib2

DEFAULT_HOSTNAME = "openconnecto.me"
DEFAULT_PROTOCOL = "http"


class neurodata(Remote):

    # SECTION:
    # Enumerables

    IMAGE = IMG = 'image'
    ANNOTATION = ANNO = 'annotation'

    def __init__(self, hostname=DEFAULT_HOSTNAME, protocol=DEFAULT_PROTOCOL,
                       meta_root="http://lims.neurodata.io/", meta_protocol=DEFAULT_PROTOCOL):
        # Prepare meta url
        self.meta_root = meta_root
        if not self.meta_root.endswith('/'):
            self.meta_root = self.meta_root + "/"
        if self.meta_root.startswith('http'):
            self.meta_root = self.meta_root[self.meta_root.index('://')+3:]
        self.meta_protocol = meta_protocol
        super(neurodata, self).__init__(hostname, protocol)

    def ping(self):
        """
        Returns the status-code of the API (estimated using the public-tokens
        lookup page).

        Arguments:
            None

        Returns:
            int status code
        """
        return super(neurodata, self).ping('public_tokens/')

    def url(self, suffix=""):
        return super(neurodata, self).url('/ocp/ca/' + suffix)

    def meta_url(self, suffix=""):
        return self.meta_protocol + "://" + self.meta_root + suffix

    def __repr__(self):
        """
        Returns a string representation that can be used to reproduce this
        instance. `eval(repr(this))` should return an identical copy.

        Arguments:
            None

        Returns:
            str: Representation of reproducible instance.
        """
        return "ndio.remote.neurodata('{}', '{}')".format(
            self.hostname,
            self.protocol,
            self.meta_url,
            self.meta_protocol
        )

    # SECTION:
    # Metadata

    def get_public_tokens(self):
        """
        Get a list of public tokens available on this server.

        Arguments:
            None

        Returns:
            str[]: list of public tokens
        """
        r = requests.get(self.url() + "public_tokens/")
        return r.json()

    def get_public_datasets(self):
        """
        NOTE: VERY SLOW!
        Gets a list of public datasets. Different than public tokens!

        Arguments:
            None

        Returns:
            str[]: list of public datasets
        """
        return list(self.get_public_datasets_and_tokens().keys())

    def get_public_datasets_and_tokens(self):
        """
        NOTE: VERY SLOW!
        Gets a dictionary relating key:dataset to value:[tokens] that rely
        on that dataset.

        Arguments:
            None

        Returns:
            dict: relating key:dataset to value:[tokens]
        """
        datasets = {}
        tokens = self.get_public_tokens()
        for t in tokens:
            dataset = self.get_token_dataset(t)
            if dataset in datasets:
                datasets[dataset].append(t)
            else:
                datasets[dataset] = [t]
        return datasets

    def get_token_dataset(self, token):
        """
        Get the dataset for a given token.

        Arguments:
            token (str): The token to inspect

        Returns:
            str: The name of the dataset
        """
        return self.get_proj_info(token)['dataset']['description']

    def get_proj_info(self, token):
        """
        Returns the project info for a given token.

        Arguments:
            token (str): Token to return information for

        Returns:
            JSON: representation of proj_info
        """
        r = requests.get(self.url() + "{}/info/".format(token))
        r_dict = r.json()
        r_dict['metadata'] = self._lims_shim_get_metadata(token)
        return r_dict


    def get_token_info(self, token):
        """
        An alias for get_proj_info.
        """
        return self.get_proj_info(token)

    def get_channels(self, token):
        """
        Wraps get_proj_info to return a dictionary of just the channels of
        a given project.

        Arguments:
            token (str): Token to return channels for

        Returns:
            JSON: dictionary of channels.
        """
        return self.get_proj_info(token)['channels']

    def get_image_size(self, token, resolution=0):
        """
        Returns the size of the volume (3D). Convenient for when you want
        to download the entirety of a dataset.

        Arguments:
            token (str): The token for which to find the dataset image bounds
            resolution (int : 0): The resolution at which to get image bounds.
                Defaults to 0, to get the largest area available.

        Returns:
            int[3]: The size of the bounds. Should == get_volume.shape

        Raises:
            RemoteDataNotFoundError: If the token is invalid, or if the
                metadata at that resolution is unavailable in projinfo.
        """
        info = self.get_token_info(token)
        res = str(resolution)
        if res not in info['dataset']['imagesize']:
            raise RemoteDataNotFoundError("Resolution " + res +
                                          " is not available.")
        return info['dataset']['imagesize'][str(resolution)]

    """
    OCPMeta Remotes enable access to the Metadata-OCP API endpoints (which
    can be found at `api.neurodata.io/metadata/ocp/`). This class is useful
    for reading metadata for existing projects, or, if you specify a key,
    adding new metadata, manipulating your existing metadata, and archiving
    old metadata. Archived metadata is still stored by the LIMS system.
    """

    def _lims_shim_get_metadata(self, token):
        """
        Get metadata via a project token.

        Arguments:
            token (str):      The project (token) to access

        Returns:
            JSON metadata associated with this project
        """

        req = requests.get(self.meta_url("metadata/ocp/get/" + token))
        return req.json()

    def set_metadata(self, token, data):
        """
        Insert new metadata into the OCP metadata database.

        Arguments:
            token (str): Token of the datum to set
            data (str): A dictionary to insert as metadata. Include `secret`.

        Returns:
            JSON of the inserted ID (convenience) or an error message.

        Throws:
            RemoteDataUploadError: If the token is already populated, or if
                there is an issue with your specified `secret` key.
        """
        req = requests.post(self.meta_url("metadata/ocp/set/" + token),
                            json=data)

        if req.status_code != 200:
            raise RemoteDataUploadError(
                "Could not upload metadata: " + req.json()['message']
            )
        return req.json()

    # Image Download

    def get_image_offset(self, token, resolution=0):
        """
        Gets the image offset for a given token at a given resolution. For
        instance, the `kasthuri11` dataset starts at (0, 0, 1), so its 1850th
        slice is slice 1850, not 1849. When downloading a full dataset, the
        result of this function should be your x/y/z starts.

        Arguments:
            token (str): The token to inspect
            resolution (int : 0): The resolution at which to gather the offset

        Returns:
            int[3]: The origin of the dataset, as a list
        """
        info = self.get_token_info(token)
        res = str(resolution)
        if res not in info['dataset']['offset']:
            raise RemoteDataNotFoundError("Resolution " + res +
                                          " is not available.")
        return info['dataset']['offset'][str(resolution)]

    # SECTION:
    # Data Download

    def get_xy_slice(self, token, channel,
                     x_start, x_stop,
                     y_start, y_stop,
                     z_index,
                     resolution=0):
        """
        Return a binary-encoded, decompressed 2d image. You should
        specify a 'token' and 'channel' pair.  For image data, users
        should use the channel 'image.'

        Arguments:
            token (str): Token to identify data to download
            channel (str): Channel
            resolution (int): Resolution level
            Q_start (int):` The lower bound of dimension 'Q'
            Q_stop (int): The upper bound of dimension 'Q'
            z_index (int): The z-slice to image

        Returns:
            str: binary image data
        """
        im = self._get_cutout_no_chunking(token, channel, resolution,
                                          x_start, x_stop, y_start, y_stop,
                                          z_index, z_index+1)[0]
        return im

    def get_image(self, token, channel,
                  x_start, x_stop,
                  y_start, y_stop,
                  z_index,
                  resolution=0):
        """
        Alias for the `get_xy_slice` function for backwards compatibility.
        """
        return self.get_xy_slice(token, channel,
                                 x_start, x_stop,
                                 y_start, y_stop,
                                 z_index,
                                 resolution)

    def get_volume(self, token, channel,
                   x_start, x_stop,
                   y_start, y_stop,
                   z_start, z_stop,
                   resolution=1,
                   block_size=(256, 256, 16),
                   crop=False,
                   memory=True):
        """
        Get a RAMONVolume volumetric cutout from the neurodata server.

        Arguments:
            token (str): Token to identify data to download
            channel (str): Channel
            resolution (int): Resolution level
            Q_start (int):` The lower bound of dimension 'Q'
            Q_stop (int): The upper bound of dimension 'Q'
            block_size (int[3]): Block size of this dataset
            crop (bool): whether or not to crop the volume before returning it
            memory (bool): if true, use blosc and return a numpy array without
                           writing to disk.  Good for image only requests that
                           fit in RAM.  Otherwise use existing hdf5 interface.
                           TODO: chunking for this mode
        Returns:
            ndio.ramon.RAMONVolume: Downloaded data.

        Raises:
            NotImplementedError: If you try to crop... Sorry :(
        """

        size = (x_stop-x_start)*(y_stop-y_start)*(z_stop-z_start)
        volume = ramon.RAMONVolume()
        volume.xyz_offset = [x_start, y_start, z_start]
        volume.resolution = resolution

        volume.cutout = self.get_cutout(token, channel, x_start,
                                        x_stop, y_start, y_stop,
                                        z_start, z_stop,
                                        resolution=resolution,
                                        memory=memory)
        return volume

    def get_cutout(self, token, channel,
                   x_start, x_stop,
                   y_start, y_stop,
                   z_start, z_stop,
                   resolution=1,
                   block_size=(256, 256, 16),
                   memory=True):
        """
        Get volumetric cutout data from the neurodata server.

        Arguments:
            token (str): Token to identify data to download
            channel (str): Channel
            resolution (int): Resolution level
            Q_start (int):` The lower bound of dimension 'Q'
            Q_stop (int): The upper bound of dimension 'Q'
            block_size (int[3]): Block size of this dataset

        Returns:
            numpy.ndarray: Downloaded data.
        """

        size = (x_stop-x_start)*(y_stop-y_start)*(z_stop-z_start)

        if size < 2E9 and memory and six.PY2:  # TODO
            return self._get_cutout_blosc_no_chunking(token, channel,
                                                      resolution, x_start,
                                                      x_stop, y_start, y_stop,
                                                      z_start, z_stop)
        else:
            # For now, max out at 512MB
            if True:  # size < 1E9 / 2:
                return self._get_cutout_no_chunking(token, channel, resolution,
                                                    x_start, x_stop,
                                                    y_start, y_stop,
                                                    z_start, z_stop)

            else:
                # Get an array-of-tuples of blocks to request.
                from ndio.utils.parallel import block_compute, snap_to_cube
                blocks = block_compute(x_start, x_stop,
                                       y_start, y_stop,
                                       z_start, z_stop)

                vol = numpy.zeros(((y_stop - y_start) + 1,
                                  (x_stop - x_start) + 1,
                                  (z_stop - z_start) + 1))
                for b in blocks:
                    data = self._get_cutout_no_chunking(
                                         token, channel, resolution,
                                         b[0][0], b[0][1],
                                         b[1][0], b[1][1],
                                         b[2][0], b[2][1])
                    data = numpy.rollaxis(data, 0, 3)
                    vol[b[1][0]:b[1][1], b[0][0]:b[0][1], b[2][0]:b[2][1]] = data

                return vol

    def _get_cutout_no_chunking(self, token, channel, resolution,
                                x_start, x_stop, y_start, y_stop,
                                z_start, z_stop):
        url = self.url() + "{}/{}/hdf5/{}/{},{}/{},{}/{},{}/".format(
           token, channel, resolution,
           x_start, x_stop,
           y_start, y_stop,
           z_start, z_stop
        )
        req = requests.get(url)
        if req.status_code is not 200:
            raise IOError("Bad server response for {}: {}: {}".format(
                          url,
                          req.status_code,
                          req.text))

        with tempfile.NamedTemporaryFile() as tmpfile:
            tmpfile.write(req.content)
            tmpfile.seek(0)
            h5file = h5py.File(tmpfile.name, "r")
            return h5file.get(channel).get('CUTOUT')[:]
        raise IOError("Failed to make tempfile.")

    def _get_cutout_blosc_no_chunking(self, token, channel, resolution,
                                      x_start, x_stop, y_start, y_stop,
                                      z_start, z_stop):

        url = self.url() + "{}/{}/blosc/{}/{},{}/{},{}/{},{}/".format(
           token, channel, resolution,
           x_start, x_stop,
           y_start, y_stop,
           z_start, z_stop
        )
        req = requests.get(url)
        if req.status_code is not 200:
            raise IOError("Bad server response for {}: {}: {}".format(
                          url,
                          req.status_code,
                          req.text))

        return blosc.unpack_array(req.content)[0]  # TODO: 4D - 3D array

        raise IOError("Failed to retrieve blosc cutout.")

    # SECTION:
    # Data Upload

    def post_cutout(self, token, channel,
                    x_start, x_stop,
                    y_start, y_stop,
                    z_start, z_stop,
                    data,
                    dtype='',
                    resolution=0,
                    roll_axis=False):
        """
        Post a cutout to the server.

        Arguments:
            token (str)
            channel (str)
            q_start (int)
            q_stop (int)
            data:           A numpy array of data. Pass in (x, y, z)
            resolution:     Default resolution of the data
            roll_axis:      Default True. Pass True if you're supplying data
                            in (z, x, y) order. maybe.
            dtype:          Pass in datatype if you know it. Otherwise we'll
                            check the projinfo.
        Returns:
            bool: True on success

        Raises:
            RemoteDataUploadError if there's an issue during upload.
        """

        datatype = self.get_proj_info(token)['channels'][channel]['datatype']
        if data.dtype.name != datatype:
            data = data.astype(datatype)

        if roll_axis:
            # put the z-axis first
            data = numpy.rollaxis(data, 2)

        data = numpy.expand_dims(data, axis=0)
        tempfile = StringIO()
        numpy.save(tempfile, data)

        compressed = zlib.compress(tempfile.getvalue())

        url = self.url() + "{}/{}/npz/{}/{},{}/{},{}/{},{}/".format(
            token, channel,
            resolution,
            x_start, x_stop,
            y_start, y_stop,
            z_start, z_stop
        )

        req = requests.post(url, data=compressed, headers={
            'Content-Type': 'application/octet-stream'
        })

        if req.status_code is not 200:
            raise RemoteDataUploadError(req.text)
        else:
            return True

    # SECTION:
    # RAMON Download

    def get_ramon_ids(self, token, channel='annotation', ramon_type=None):
        """
        Return a list of all IDs available for download from this token and
        channel.

        Arguments:
            token (str): Project to use
            channel (str): Channel to use (default 'annotation')
        Returns:
            int[]: A list of the ids of the returned RAMON objects
        Raises:
            RemoteDataNotFoundError: If the channel or token is not found
        """

        url = self.url("{}/{}/query/".format(token, channel))
        if ramon_type is not None:
            # User is requesting a specific ramon_type.
            if type(ramon_type) is not int:
                ramon_type = ramon.AnnotationType.get_int(ramon_type)
            url += "type/{}/".format(str(ramon_type))

        req = requests.get(url)

        if req.status_code is not 200:
            raise RemoteDataNotFoundError('No query results for token {}.'
                                          .format(token))
        else:
            with tempfile.NamedTemporaryFile() as tmpfile:
                tmpfile.write(req.content)
                tmpfile.seek(0)
                h5file = h5py.File(tmpfile.name, "r")
                return [i for i in h5file['ANNOIDS']]
            raise IOError("Could not successfully mock HDF5 file for parsing.")

    def get_ramon(self, token, channel, ids, resolution,
                  metadata_only=False, sieve=None, batch_size=100):
        """
        Download a RAMON object by ID.

        Arguments:
            token (str): Project to use
            channel (str): The channel to use
            ids (int, str, int[], str[]): The IDs of a RAMON object to gather.
                Can be int (3), string ("3"), int[] ([3, 4, 5]), or string
                (["3", "4", "5"]).
            resolution (int): Resolution
            metadata_only (bool : False):  If True, returns get_ramon_metadata
            sieve (function : None): A function that accepts a single ramon
                and returns True or False depending on whether you want that
                ramon object to be included in your response or not.

                For example,

                ```
                def is_even_id(ramon):
                    return ramon.id % 2 == 0
                ```

                You can then pass this to get_ramon like this:

                ```
                ndio.remote.neurodata.get_ramon( . . . , sieve=is_even_id)
                ```
            batch_size (int : 100): The amount of RAMON objects to download at
                a time. If this is greater than 100, we anticipate things going
                very poorly for you. So if you set it <100, ndio will use it.
                If >=100, set it to 100.


        Returns:
            ndio.ramon.RAMON[]

        Raises:
            RemoteDataNotFoundError: If the requested ids cannot be found.
        """

        b_size = min(100, batch_size)

        if metadata_only:
            return self.get_ramon_metadata(token, channel, ids)

        BATCH = False
        if type(ids) is int:
            ids = [ids]
        if type(ids) is list:
            ids = [str(i) for i in ids]
            if len(ids) > b_size:
                BATCH = True
        # now ids is a list of strings

        if BATCH:
            rs = []
            id_batches = [ids[i:i+b_size] for i in xrange(0, len(ids), b_size)]
            for batch in id_batches:
                rs.extend(self._get_ramon_batch(token, channel,
                                                batch, resolution))
        else:
            rs = self._get_ramon_batch(token, channel, ids, resolution)

        if sieve is not None:
            return [r for r in rs if sieve(r)]
        return rs

    def _get_ramon_batch(self, token, channel, ids, resolution):
        url = self.url("{}/{}/{}/cutout/{}/".format(token, channel,
                                                    ",".join(ids), resolution))
        req = requests.get(url)

        if req.status_code is not 200:
            raise RemoteDataNotFoundError('No data for id {}.'.format(ids))
        else:

            with tempfile.NamedTemporaryFile() as tmpfile:
                tmpfile.write(req.content)
                tmpfile.seek(0)
                h5file = h5py.File(tmpfile.name, "r")

                rs = [ramon.hdf5_to_ramon(h5file, i) for i in ids]
                return rs

    def get_ramon_metadata(self, token, channel, anno_id):
        """
        Download a RAMON object by ID. `anno_id` can be a string `"123"`, an
        int `123`, an array of ints `[123, 234, 345]`, an array of strings
        `["123", "234", "345"]`, or a comma-separated string list
        `"123,234,345"`.

        Arguments:
            token (str): Project to use
            channel (str): The channel to use
            anno_id: An int, a str, or a list of ids to gather

        Returns:
            JSON. If you pass a single id in str or int, returns a single datum
            If you pass a list of int or str or a comma-separated string, will
            return a dict with keys from the list and the values are the JSON
            returned from the server.

        Raises:
            RemoteDataNotFoundError: If the data cannot be found on the Remote
        """

        if type(anno_id) is int:
            # there's just one ID to download
            return self._get_single_ramon_metadata(token, channel,
                                                   str(anno_id))
        elif type(anno_id) is str:
            # either "id" or "id,id,id":
            if (len(anno_id.split(',')) > 1):
                results = {}
                for i in anno_id.split(','):
                    results[i] = self._get_single_ramon_metadata(
                        token, channel, anno_id.strip()
                    )
                return results
            else:
                # "id"
                return self._get_single_ramon_metadata(token, channel,
                                                       anno_id.strip())
        elif type(anno_id) is list:
            # [id, id] or ['id', 'id']
            results = []
            for i in anno_id:
                results.append(self._get_single_ramon_metadata(token, channel,
                                                               str(i)))
            return results

    def _get_single_ramon_metadata(self, token, channel, anno_id):
        req = requests.get(self.url() +
                           "{}/{}/{}/nodata/".format(token, channel,
                                                     anno_id))

        if req.status_code is not 200:
            raise RemoteDataNotFoundError('No data for id {}.'.format(anno_id))
        else:
            with tempfile.NamedTemporaryFile() as tmpfile:
                tmpfile.write(req.content)
                tmpfile.seek(0)
                h5file = h5py.File(tmpfile.name, "r")

                r = ramon.hdf5_to_ramon(h5file)
                return r

    def reserve_ids(self, quantity):
        """
        Requests a list of next-available-IDs from the server.

        Arguments:
            quantity (int): The number of IDs to reserve

        Returns:
            int[quantity]: List of IDs you've been granted
        """
        raise NotImplementedError("No reserving yet, sorry!")

    def merge_ids(self, token, channel, ids, delete=False):
        """
        Call the restful endpoint to merge two RAMON objects into one.

        Arguments:
            token
            channel
            ids (int[]): the list of the IDs to merge
            delete (bool : False): Whether to delete after merging.

        Returns:
            json
        """
        req = requests.get(self.url() + "/merge/{}/"
                           .format(','.join([str(i) for i in ids])))
        if req.status_code is not 200:
            raise RemoteDataUploadError('Could not merge ids {}'.format(
                                        ','.join([str(i) for i in ids])))
        if delete:
            self.delete_ramon(token, channel, ids[1:])
        return True

    def delete_ramon(self, token, channel, anno):
        """
        Deletes an annotation from the server. Probably you should be careful
        with this function, it seems dangerous.

        Arguments:
            token
            channel
            anno (int OR list(int) OR RAMON): The annotation to delete. If a
                RAMON object is supplied, the remote annotation will be deleted
                by an ID lookup. If an int is supplied, the annotation will be
                deleted for that ID. If a list of ints are provided, they will
                all be deleted.

        Returns:
            bool: Success
        """
        if type(anno) is int:
            a = anno
        if type(anno) is str:
            a = int(anno)
        if type(anno) is list:
            a = ",".join(anno)
        else:
            a = anno.id

        req = requests.delete(self.url("{}/{}/{}/".format(token, channel, a)))
        if req.status_code is not 200:
            raise RemoteDataNotFoundError("Could not delete id {}: {}"
                                          .format(a, req.text))
        else:
            return True

    def post_ramon(self, token, channel, r, batch_size=100):
        """
        Posts a RAMON object to the Remote.

        Arguments:
            token (str): Project to use
            channel (str): The channel to use
            r (RAMON or RAMON[]): The annotation(s) to upload
            batch_size (int : 100): The number of RAMONs to post simultaneously
                at maximum in one file. If len(r) > batch_size, the batch will
                be split and uploaded automatically. Must be less than 100.

        Returns:
            bool: Success = True

        Throws:
            RemoteDataUploadError if something goes wrong
        """

        # Max out batch-size at 100.
        b_size = min(100, batch_size)

        # Coerce incoming IDs to a list.
        if type(r) is not list:
            r = [r]

        # If there are too many to fit in one batch, split here and call this
        # function recursively.
        if len(r) > batch_size:
            batches = [r[i:i+b_size] for i in xrange(0, len(r), b_size)]
            for batch in batches:
                self.post_ramon(token, channel, batch, b_size)
            return

        with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
            for i in r:
                tmpfile = ramon.ramon_to_hdf5(i, tmpfile)

            url = self.url("{}/{}/".format(token, channel))
            files = {'file': ('ramon.hdf5', open(tmpfile.name, 'rb'))}
            res = requests.post(url, files=files)

            if res.status_code == 404:
                raise RemoteDataUploadError('[400] Could not upload {}'
                                            .format(str(r)))
            if res.status_code == 500:
                raise RemoteDataUploadError('[500] Could not upload {}'
                                            .format(str(r)))

        return True

    # SECTION:
    # Channels

    def create_channel(self, token, name, channel_type, dtype, readonly):
        """
        Create a new channel on the Remote, using channel_data.

        Arguments:
            token (str): The token the new channel should be added to
            name (str): The name of the channel to add
            type (str): Type of the channel to add (e.g. neurodata.IMAGE)
            dtype (str): The datatype of the channel's data (e.g. 'uint8')
            readonly (bool): Can others write to this channel?

        Returns:
            bool: True if successful, False otherwise.

        Raises:
            ValueError: If your args were bad :(
            RemoteDataUploadError: If the channel data is valid but upload
                fails for some other reason.
        """

        for c in name:
            if not c.isalnum():
                raise ValueError("Name cannot contain character {}.".format(c))

        if channel_type not in ['image', 'annotation']:
            raise ValueError('Channel type must be ' +
                             'neurodata.IMAGE or neurodata.ANNOTATION.')

        if readonly * 1 not in [0, 1]:
            raise ValueError("readonly must be 0 (False) or 1 (True).")

        # Good job! You supplied very nice arguments.
        req = requests.post(self.url("{}/createChannel/".format(token)), json={
            "channels": {
                name: {
                    "channel_name": name,
                    "channel_type": channel_type,
                    "datatype": dtype,
                    "readonly": readonly * 1
                }
            }
        })

        if req.status_code is not 200:
            raise RemoteDataUploadError('Could not upload {}'.format(req.text))
        else:
            return True

    def delete_channel(self, token, name):
        """
        Delete an existing channel on the Remote. Be careful!

        Arguments:
            token (str): The token the new channel should be deleted from
            name (str): The name of the channel to delete

        Returns:
            bool: True if successful, False otherwise.

        Raises:
            RemoteDataUploadError: If the upload fails for some reason.
        """

        req = requests.post(self.url("{}/deleteChannel/".format(token)), json={
            "channels": [name]
        })

        if req.status_code is not 200:
            raise RemoteDataUploadError('Could not delete {}'.format(req.text))
        else:
            return True
