#!/usr/bin/python
#
# Copyright (C) 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""SpreadsheetsService extends the GDataService to streamline Google
Spreadsheets operations.

  SpreadsheetService: Provides methods to query feeds and manipulate items.
                      Extends GDataService.

  DictionaryToParamList: Function which converts a dictionary into a list of
                         URL arguments (represented as strings). This is a
                         utility function used in CRUD operations.
"""

__author__ = 'api.laurabeth@gmail.com (Laura Beth Lincoln)'


import gdata
import atom.service
import gdata.service
import gdata.spreadsheet
import atom


class Error(Exception):
  """Base class for exceptions in this module."""
  pass


class RequestError(Error):
  pass


class SpreadsheetsService(gdata.service.GDataService):
  """Client for the Google Spreadsheets service."""

  def __init__(self, email=None, password=None, source=None,
               server='spreadsheets.google.com', additional_headers=None,
               **kwargs):
    """Creates a client for the Google Spreadsheets service.

    Args:
      email: string (optional) The user's email address, used for
          authentication.
      password: string (optional) The user's password.
      source: string (optional) The name of the user's application.
      server: string (optional) The name of the server to which a connection
          will be opened. Default value: 'spreadsheets.google.com'.
      **kwargs: The other parameters to pass to gdata.service.GDataService
          constructor.
    """
    gdata.service.GDataService.__init__(
        self, email=email, password=password, service='wise', source=source,
        server=server, additional_headers=additional_headers, **kwargs)

  def GetSpreadsheetsFeed(self, key=None, query=None, visibility='private', 
      projection='full'):
    """Gets a spreadsheets feed or a specific entry if a key is defined
    Args:
      key: string (optional) The spreadsheet key defined in /ccc?key=
      query: DocumentQuery (optional) Query parameters
      
    Returns:
      If there is no key, then a SpreadsheetsSpreadsheetsFeed.
      If there is a key, then a SpreadsheetsSpreadsheet.
    """
    
    uri = ('http://%s/feeds/spreadsheets/%s/%s' 
           % (self.server, visibility, projection))
    
    if key is not None:
      uri = '%s/%s' % (uri, key)
      
    if query != None:
      query.feed = uri
      uri = query.ToUri()

    if key:
      return self.Get(uri, 
          converter=gdata.spreadsheet.SpreadsheetsSpreadsheetFromString)
    else:
      return self.Get(uri,
          converter=gdata.spreadsheet.SpreadsheetsSpreadsheetsFeedFromString)
  
  def GetWorksheetsFeed(self, key, wksht_id=None, query=None, 
      visibility='private', projection='full'):
    """Gets a worksheets feed or a specific entry if a wksht is defined
    Args:
      key: string The spreadsheet key defined in /ccc?key=
      wksht_id: string (optional) The id for a specific worksheet entry
      query: DocumentQuery (optional) Query parameters
      
    Returns:
      If there is no wksht_id, then a SpreadsheetsWorksheetsFeed.
      If there is a wksht_id, then a SpreadsheetsWorksheet.
    """
    
    uri = ('http://%s/feeds/worksheets/%s/%s/%s' 
           % (self.server, key, visibility, projection))
    
    if wksht_id != None:
      uri = '%s/%s' % (uri, wksht_id)
      
    if query != None:
      query.feed = uri
      uri = query.ToUri()

    if wksht_id:
      return self.Get(uri, 
          converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString)
    else:
      return self.Get(uri,
          converter=gdata.spreadsheet.SpreadsheetsWorksheetsFeedFromString)

  def AddWorksheet(self, title, row_count, col_count, key):
    """Creates a new worksheet in the desired spreadsheet.

    The new worksheet is appended to the end of the list of worksheets. The
    new worksheet will only have the available number of columns and cells 
    specified.

    Args:
      title: str The title which will be displayed in the list of worksheets.
      row_count: int or str The number of rows in the new worksheet.
      col_count: int or str The number of columns in the new worksheet.
      key: str The spreadsheet key to the spreadsheet to which the new 
          worksheet should be added. 

    Returns:
      A SpreadsheetsWorksheet if the new worksheet was created succesfully.  
    """
    new_worksheet = gdata.spreadsheet.SpreadsheetsWorksheet(
        title=atom.Title(text=title), 
        row_count=gdata.spreadsheet.RowCount(text=str(row_count)), 
        col_count=gdata.spreadsheet.ColCount(text=str(col_count)))
    return self.Post(new_worksheet, 
        'http://%s/feeds/worksheets/%s/private/full' % (self.server, key),
        converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString)

  def UpdateWorksheet(self, worksheet_entry, url=None):
    """Changes the size and/or title of the desired worksheet.
    
    Args:
      worksheet_entry: SpreadsheetWorksheet The new contents of the 
          worksheet. 
      url: str (optional) The URL to which the edited worksheet entry should
          be sent. If the url is None, the edit URL from the worksheet will
          be used.

    Returns: 
      A SpreadsheetsWorksheet with the new information about the worksheet.
    """
    target_url = url or worksheet_entry.GetEditLink().href
    return self.Put(worksheet_entry, target_url, 
        converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString)
    
  def DeleteWorksheet(self, worksheet_entry=None, url=None):
    """Removes the desired worksheet from the spreadsheet
    
    Args:
      worksheet_entry: SpreadsheetWorksheet (optional) The worksheet to
          be deleted. If this is none, then the DELETE reqest is sent to 
          the url specified in the url parameter.
      url: str (optaional) The URL to which the DELETE request should be
          sent. If left as None, the worksheet's edit URL is used.

    Returns:
      True if the worksheet was deleted successfully. 
    """
    if url:
      target_url = url
    else:
      target_url = worksheet_entry.GetEditLink().href
    return self.Delete(target_url)
  
  def GetCellsFeed(self, key, wksht_id='default', cell=None, query=None, 
      visibility='private', projection='full'):
    """Gets a cells feed or a specific entry if a cell is defined
    Args:
      key: string The spreadsheet key defined in /ccc?key=
      wksht_id: string The id for a specific worksheet entry
      cell: string (optional) The R1C1 address of the cell
      query: DocumentQuery (optional) Query parameters
      
    Returns:
      If there is no cell, then a SpreadsheetsCellsFeed.
      If there is a cell, then a SpreadsheetsCell.
    """
    
    uri = ('http://%s/feeds/cells/%s/%s/%s/%s' 
           % (self.server, key, wksht_id, visibility, projection))
    
    if cell != None:
      uri = '%s/%s' % (uri, cell)
      
    if query != None:
      query.feed = uri
      uri = query.ToUri()

    if cell:
      return self.Get(uri, 
          converter=gdata.spreadsheet.SpreadsheetsCellFromString)
    else:
      return self.Get(uri, 
          converter=gdata.spreadsheet.SpreadsheetsCellsFeedFromString)
  
  def GetListFeed(self, key, wksht_id='default', row_id=None, query=None,
      visibility='private', projection='full'):
    """Gets a list feed or a specific entry if a row_id is defined
    Args:
      key: string The spreadsheet key defined in /ccc?key=
      wksht_id: string The id for a specific worksheet entry
      row_id: string (optional) The row_id of a row in the list
      query: DocumentQuery (optional) Query parameters
      
    Returns:
      If there is no row_id, then a SpreadsheetsListFeed.
      If there is a row_id, then a SpreadsheetsList.
    """
    
    uri = ('http://%s/feeds/list/%s/%s/%s/%s' 
           % (self.server, key, wksht_id, visibility, projection))

    if row_id is not None:
      uri = '%s/%s' % (uri, row_id)
      
    if query is not None:
      query.feed = uri
      uri = query.ToUri()

    if row_id:
      return self.Get(uri, 
          converter=gdata.spreadsheet.SpreadsheetsListFromString)
    else:
      return self.Get(uri, 
          converter=gdata.spreadsheet.SpreadsheetsListFeedFromString)
    
  def UpdateCell(self, row, col, inputValue, key, wksht_id='default'):
    """Updates an existing cell.
    
    Args:
      row: int The row the cell to be editted is in
      col: int The column the cell to be editted is in
      inputValue: str the new value of the cell
      key: str The key of the spreadsheet in which this cell resides.
      wksht_id: str The ID of the worksheet which holds this cell.
      
    Returns:
      The updated cell entry
    """
    row = str(row)
    col = str(col)