#! /usr/bin/python
# -*- coding: iso-8859-15 -*-

##
## importmodule_vibez_musiclog.py
##  - "musiclog" import module for It'sGotTheVibez, an last.fm offline
##    scrobbler for Trekstor's "Vibez" portable music player.
##
##    This is the recommended and most accurate way to import listeing
##    data from the TrekStor Vibez. It works with the player firmware
##    version 1.15 and later.
##    You need to enable the "music.log" file in the player's "advanced
##    settings" menu.
##
## See http://www.ohrner.net/ for latest news and updates, please.
## 
## $Id$
## $URL$
## 
## Copyright (C) 2008 Gunter Ohrner "gunter _(@)_ ohrner.net"
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
##


import os

import itsgotthevibezlib
import lastfmsubmitter
import time
import utilitylib


debug = True
debug = False

#################################################################
### Public Import Module API
#################################################################


IMPORT_MODULE_NAME = 'musiclog'


def publishOptions(option_parser, default_config_value_dict):
	parser = option_parser
	default_config_value_dict['musiclog_file'] = 'music.log'
	parser.add_option(
		'-f', '--musiclog-file', dest='musiclog_file',
		help=(('[%s] Path/filename of your Trekstor Vibez\' "music.log" file. '
					 'This file can be enabled on the Vibez in '
					 'MENU -> Settings -> Advanced -> Listening Log in firmware '
					 'version 1.15 or newer. '
					 'The default value for this option is "%s". '
					 % (IMPORT_MODULE_NAME,
							default_config_value_dict['musiclog_file']))
					+ 'ATTENTION: After a successfull import, this file will be '
					'deleted by default.'),
		metavar='MUSICLOGFILE')
	parser.add_option(
		'-k', '--keep-music-log', dest='keep_music_log',
		help=('[%s] Specify this option if you do not want It\'sGotTheVibez '
					'to delete your music.log file after a successfully importing '
					'it. It\'s generally recommended that you delete the file '
					'to avoid importing the same listening events twice.') \
		% (IMPORT_MODULE_NAME),
		action='store_true')



def initImportModule(scrobbler_cfg, persistent_state):
	runstate = RuntimeState()

	runstate.musiclog_handle = None

	## update runtime state (not persistent between runs)
	checkAndPrepareRunEnvironment(scrobbler_cfg, runstate)

	if runstate.music_log_exists:
		runstate.musiclog_handle = open(runstate.musiclog_file, 'r')

	return (runstate, persistent_state)



def importListeningEvents(scrobbler_cfg,
													runstate,
													persistent_state):
	if not scrobbler_cfg.remove_pending_events \
			 and runstate.musiclog_handle is not None:
		## do not just remove the logfile, also import its contents
		new_submission_records = processMusicLog(runstate.musiclog_handle)
	else:
		## There's nothing to see here, please move along!
		new_submission_records = []

	utilitylib.safeClose(runstate.musiclog_handle)
	
	if not scrobbler_cfg.keep_music_log and not scrobbler_cfg.dry_run \
			 and runstate.music_log_exists:
		os.unlink(runstate.musiclog_file)

	return new_submission_records




#################################################################
## Private Module Implementation
#################################################################



def checkAndPrepareRunEnvironment(scrobbler_cfg,
																	runtime_state):
	if scrobbler_cfg.musiclog_file is None:
		raise itsgotthevibezlib.ItsGotTheVibezConfigurationException(
			'No listening log file ("music.log") was specified. '
			'The musiclog import '
			'module must know where to find the log file.')
	runtime_state.musiclog_file = os.path.expanduser(scrobbler_cfg.musiclog_file)
	
	if os.path.exists(runtime_state.musiclog_file):
		runtime_state.music_log_exists = True
	else:
		runtime_state.music_log_exists = False
		print 'Warning: The specified listening log file "%s" doesn\'t exist!' \
					% runtime_state.musiclog_file



class RuntimeState( object ):
	def __init__(self):
		self.musiclog_file = None
		self.musiclog_handle = None
		self.music_log_exists = None


class ImPersistentStateImpl( itsgotthevibezlib.AbstractImportModuleState ):

	def __init__(self):
		itsgotthevibezlib.AbstractImportModuleState.__init__(self, IMPORT_MODULE_NAME)
		## Import module state object version identifier
		self.version = 1
		## Mh, this module has no persistent state so far.
		## We could have left out this class completely, we just create
		## an instance of it and return it to the scrobbler so you have
		## some example code to build upon if you decide to use this
		## module as a starting point for your own listening event
		## import module. ;)



idx_type = 0
idx_timestamp = 1
idx_artist = 2
idx_trackname = 3
idx_album = 4
idx_trackno = 5
idx_duration = 6
idx_rating = 7



def getSubmitRecordForMusicLogRow(prev_track_end_timestamp, csv_row_string):
	listening_record = csv_row_string.split(',')

	INVALID_RECORD = (None, prev_track_end_timestamp)

	if len(listening_record) != 8:
		print 'music log data format error, illegal line, skipping: "%s"' \
					% csv_row_string
		return INVALID_RECORD

	if len(listening_record[idx_type]) != 2 \
		 or listening_record[idx_type][0] != 'P':
		print 'music log illegal record type header, skipping: "%s"' \
					% listening_record[idx_type]
		return INVALID_RECORD

	artist = utilitylib.noneIfEmpty(unescapeComma(listening_record[idx_artist]))
	album = utilitylib.noneIfEmpty(unescapeComma(listening_record[idx_album]))
	track_no = utilitylib.xint(listening_record[idx_trackno])
	track_name = utilitylib.noneIfEmpty(unescapeComma(listening_record[idx_trackname]))

	struct_time = time.strptime(listening_record[idx_timestamp], '%Y:%m:%d %H:%M:%S')
	listening_finished_timestamp = time.mktime(struct_time)

	duration_tuple = listening_record[idx_duration].split(':')
	if not 2 <= len(duration_tuple) <= 3:
		print 'music log listening duration error, illegal duration value, skipping: "%s"' \
					% listening_record[idx_duration]
		return INVALID_RECORD

	duration_secs = utilitylib.xint(duration_tuple[-1]) \
									+ 60*utilitylib.xint(duration_tuple[-2])
	if len(duration_tuple) == 3:
		duration_secs += 3600*utilitylib.xint(duration_tuple[-3])

	listening_timestamp = max(prev_track_end_timestamp,
														listening_finished_timestamp - duration_secs)

	listening_duration = max(0,
													 listening_finished_timestamp - prev_track_end_timestamp)

	track_submit_record = lastfmsubmitter.TrackSubmitRecord(
		artist, track_name, duration_secs, listening_timestamp,
		listening_duration, None, track_no, album, None,
		lastfmsubmitter.TrackSubmitRecord.SOURCE_USER_SELECTED)

	return (track_submit_record, listening_finished_timestamp)



def unescapeComma(strvalue):
	return strvalue.replace('&#44;', ',')



def processMusicLog(musiclog_handle):
	'''The module\'s main workhorse function.
	Read the specified music.log file and convert each row to a
	TrackSubmissionRecord.'''

	submission_record_list = []

	prev_track_end_timestamp = 0
	for csv_row_string in musiclog_handle:
		(submit_rec, prev_track_end_timestamp) \
								 = getSubmitRecordForMusicLogRow(prev_track_end_timestamp, csv_row_string)

		if submit_rec is not None:
			submission_record_list += [ submit_rec ]

			if debug:
				print submit_rec.title, ';', submit_rec.play_timestamp, ';', \
							utilitylib.formatTS(submit_rec.play_timestamp)


	## update agenda...
	return submission_record_list
