1/*
2   Project: MPDCon
3
4   Copyright (C) 2004
5
6   Author: Daniel Luederwald
7
8   Created: 2004-05-14 11:37:03 +0200 by flip
9
10   This application is free software; you can redistribute it and/or
11   modify it under the terms of the GNU General Public
12   License as published by the Free Software Foundation; either
13   version 2 of the License, or (at your option) any later version.
14
15   This application is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   Library General Public License for more details.
19
20   You should have received a copy of the GNU General Public
21   License along with this library; if not, write to the Free
22   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
23*/
24
25#include <unistd.h>
26#include "MPDController.h"
27
28/* ---------------------
29   - Private Interface -
30   ---------------------*/
31@interface MPDController(Private)
32
33- (BOOL) _doConnect;
34- (BOOL) _checkConnection;
35
36- (PlaylistItem *) _getPlaylistItemForSong: (const struct mpd_song *)anSong;
37- (unsigned int) _getAllAlbumsCount;
38- (unsigned int) _getAllArtistsCount;
39
40int _stringSort(id string1, id string2, void *context);
41@end
42
43@implementation MPDController
44
45/* --------------------------
46   - Initialization Methods -
47   --------------------------*/
48
49+ (id) sharedMPDController
50{
51  static MPDController *_sharedMPDController = nil;
52
53  if (! _sharedMPDController)
54    {
55      _sharedMPDController = [[MPDController allocWithZone: [self zone]] init];
56    }
57
58  return _sharedMPDController;
59}
60
61- (id) init
62{
63  numArtists = 0;
64  numAlbums = 0;
65
66  return self;
67}
68
69- (void) dealloc
70{
71  mpd_connection_free(mpdConnection);
72
73  [host release];
74  [port release];
75  [password release];
76  [timeout release];
77
78  [super dealloc];
79}
80
81/* ----------------------
82   - Connection Methods -
83   ----------------------*/
84
85- (BOOL) connectToServer: (NSString *)hostName
86                    port: (NSString *)portNr
87                password: (NSString *)pword
88                 timeout: (NSString *)tout
89{
90  if ((hostName) && (portNr) && (tout)) {
91      BOOL didConnect;
92
93      if (host) {
94	  [host release];
95      }
96      if (port) {
97	  [port release];
98      }
99      if (password) {
100	  [password release];
101      }
102      if (port) {
103	  [timeout release];
104      }
105      host = [hostName copy];
106      port = [portNr copy];
107      password = [pword copy];
108      timeout = [tout copy];
109
110      didConnect = [self _doConnect];
111      return didConnect;
112  } else {
113      return NO;
114  }
115}
116
117/* ----------------
118   - Play Methods -
119   ----------------*/
120
121- (void) play
122{
123  [self playSong: -1];
124}
125
126- (void) playSong: (int)theSong
127{
128  struct mpd_status *mpdStatus;
129
130  if (![self _checkConnection]) {
131      return;
132  }
133  if (theSong != -1) {
134    mpd_send_play_pos(mpdConnection, theSong);
135  } else {
136
137    mpdStatus = mpd_run_status(mpdConnection);
138    if (mpdStatus != NULL) {
139      if(mpd_status_get_state(mpdStatus) == MPD_STATE_STOP) {
140        mpd_send_play(mpdConnection);
141      } else {
142        mpd_send_toggle_pause(mpdConnection);
143      }
144      mpd_response_finish(mpdConnection);
145      mpd_status_free(mpdStatus);
146    }
147  }
148}
149
150- (void) stop
151{
152  struct mpd_status *mpdStatus;
153
154  if (! [self _checkConnection])
155    {
156      return;
157    }
158
159  mpdStatus = mpd_run_status(mpdConnection);
160  if (mpdStatus != NULL) {
161    if((mpd_status_get_state(mpdStatus) == MPD_STATE_PLAY) ||
162      (mpd_status_get_state(mpdStatus) == MPD_STATE_PAUSE)) {
163        mpd_send_stop(mpdConnection);
164        mpd_response_finish(mpdConnection);
165    }
166    mpd_status_free(mpdStatus);
167  }
168}
169
170- (void) prev
171{
172  struct mpd_status *mpdStatus;
173
174  if (! [self _checkConnection])
175    {
176      return;
177    }
178
179  mpdStatus = mpd_run_status(mpdConnection);
180  if (mpdStatus != NULL) {
181    if((mpd_status_get_state(mpdStatus) == MPD_STATE_PLAY) ||
182      (mpd_status_get_state(mpdStatus) == MPD_STATE_PAUSE)) {
183        mpd_send_previous(mpdConnection);
184        mpd_response_finish(mpdConnection);
185    }
186    mpd_status_free(mpdStatus);
187  }
188}
189
190- (void) next
191{
192  struct mpd_status *mpdStatus;
193
194  if (! [self _checkConnection])
195    {
196      return;
197    }
198
199  mpdStatus = mpd_run_status(mpdConnection);
200  if (mpdStatus != NULL) {
201    if((mpd_status_get_state(mpdStatus) == MPD_STATE_PLAY) ||
202      (mpd_status_get_state(mpdStatus) == MPD_STATE_PAUSE)) {
203        mpd_send_next(mpdConnection);
204        mpd_response_finish(mpdConnection);
205    }
206    mpd_status_free(mpdStatus);
207  }
208}
209
210- (void) toggleShuffle
211{
212  struct mpd_status *mpdStatus;
213
214  if (! [self _checkConnection])
215    {
216      return;
217  }
218
219  mpdStatus = mpd_run_status(mpdConnection);
220  if (mpdStatus != NULL) {
221    mpd_send_random(mpdConnection, (mpd_status_get_random(mpdStatus) == 0) ? 1 : 0);
222    mpd_response_finish(mpdConnection);
223    mpd_status_free(mpdStatus);
224  }
225}
226
227- (void) toggleRepeat
228{
229  struct mpd_status *mpdStatus;
230
231  if (! [self _checkConnection])
232    {
233      return;
234    }
235
236  mpdStatus = mpd_run_status(mpdConnection);
237  if (mpdStatus != NULL) {
238    mpd_send_repeat(mpdConnection, (mpd_status_get_repeat(mpdStatus) == 0) ? 1 : 0);
239    mpd_response_finish(mpdConnection);
240    mpd_status_free(mpdStatus);
241  }
242}
243
244- (void) seekToTime: (int)time
245{
246  struct mpd_status *mpdStatus;
247
248  if (! [self _checkConnection])
249    {
250      return;
251    }
252
253  mpdStatus = mpd_run_status(mpdConnection);
254  if (mpdStatus != NULL) {
255    if ((mpd_status_get_state(mpdStatus) == MPD_STATE_PLAY)
256      || (mpd_status_get_state(mpdStatus) == MPD_STATE_PAUSE)) {
257        mpd_send_seek_pos(mpdConnection, mpd_status_get_song_pos(mpdStatus), time);
258        mpd_response_finish(mpdConnection);
259    }
260  mpd_status_free(mpdStatus);
261  }
262}
263
264- (void) setVolume: (int)volume
265{
266  if (! [self _checkConnection])
267    {
268      return;
269    }
270
271  mpd_send_set_volume(mpdConnection, volume);
272
273  mpd_response_finish(mpdConnection);
274}
275
276- (void) setCrossfade: (int)cfTime
277{
278  if (! [self _checkConnection])
279    {
280      return;
281    }
282
283  mpd_send_crossfade(mpdConnection, cfTime);
284  mpd_response_finish(mpdConnection);
285}
286
287/* -----------------------
288   - Information Methods -
289   -----------------------*/
290
291- (int) getState
292{
293  int state = state_NOCONN;
294  struct mpd_status *mpdStatus;
295
296  if (! [self _checkConnection]) {
297      return state;
298  }
299  mpdStatus = mpd_run_status(mpdConnection);
300
301  if (mpdStatus != NULL) {
302    switch (mpd_status_get_state(mpdStatus)) {
303      case MPD_STATE_UNKNOWN:
304        state = state_UNKNOWN;
305        break;
306      case MPD_STATE_STOP:
307        state = state_STOP;
308        break;
309      case MPD_STATE_PLAY:
310        state = state_PLAY;
311        break;
312      case MPD_STATE_PAUSE:
313        state = state_PAUSE;
314        break;
315    }
316    mpd_status_free(mpdStatus);
317  } else {
318    state = state_UNKNOWN;
319  }
320
321  mpd_response_finish(mpdConnection);
322
323  return state;
324}
325
326- (BOOL) isRandom
327{
328  BOOL random = NO;
329  struct mpd_status *mpdStatus;
330
331  if (! [self _checkConnection]) {
332      return random;
333  }
334  mpdStatus = mpd_run_status(mpdConnection);
335  if (mpdStatus != NULL) {
336    if ((mpd_status_get_random(mpdStatus)) == 1) {
337      random = YES;
338    } else {
339      random = NO;
340    }
341    mpd_response_finish(mpdConnection);
342    mpd_status_free(mpdStatus);
343  }
344
345  return random;
346}
347
348- (BOOL) isRepeat
349{
350  BOOL repeat = NO;
351  struct mpd_status *mpdStatus;
352
353  if (! [self _checkConnection]) {
354      return repeat;
355  }
356  mpdStatus = mpd_run_status(mpdConnection);
357  if (mpdStatus != NULL) {
358    if (mpdStatus && (mpd_status_get_repeat(mpdStatus)) == 1) {
359      repeat = YES;
360    } else {
361      repeat = NO;
362    }
363    mpd_response_finish(mpdConnection);
364    mpd_status_free(mpdStatus);
365  }
366
367  return repeat;
368}
369
370- (int) getVolume
371{
372  int volume = 0;
373  struct mpd_status *mpdStatus;
374
375  if (! [self _checkConnection]) {
376      return 0;
377  }
378
379  mpdStatus = mpd_run_status(mpdConnection);
380  if (mpdStatus != NULL) {
381    volume = mpd_status_get_volume(mpdStatus);
382    mpd_response_finish(mpdConnection);
383    mpd_status_free(mpdStatus);
384  }
385
386  return volume;
387}
388
389- (int) getCrossfade
390{
391  int cfTime = 0;
392  struct mpd_status *mpdStatus;
393
394  if (! [self _checkConnection]) {
395      return 0;
396  }
397
398  mpdStatus = mpd_run_status(mpdConnection);
399
400  if (mpdStatus != NULL) {
401    cfTime = mpd_status_get_crossfade(mpdStatus);
402    mpd_status_free(mpdStatus);
403  }
404
405  return cfTime;
406}
407
408- (StatisticsItem *) getStatistics
409{
410  struct mpd_stats *mpdStats;
411  StatisticsItem *statItem;
412
413  if (! [self _checkConnection]) {
414      return nil;
415  }
416
417  mpdStats = mpd_run_stats(mpdConnection);
418  if (mpdStats != NULL) {
419    statItem = [[StatisticsItem alloc] init];
420
421    [statItem setNumberOfArtists: mpd_stats_get_number_of_artists(mpdStats)];
422    [statItem setNumberOfAlbums: mpd_stats_get_number_of_albums(mpdStats)];
423    [statItem setNumberOfSongs: mpd_stats_get_number_of_songs(mpdStats)];
424    [statItem setUptime: mpd_stats_get_uptime(mpdStats)];
425    [statItem setDbUpdatetime: mpd_stats_get_db_update_time(mpdStats)];
426    [statItem setPlaytime: mpd_stats_get_play_time(mpdStats)];
427    [statItem setDbPlaytime: mpd_stats_get_db_play_time(mpdStats)];
428
429    mpd_stats_free(mpdStats);
430    mpd_response_finish(mpdConnection);
431    return [statItem autorelease];
432  } else {
433    return nil;
434  }
435}
436
437/* ---------------------
438   - Playlist Commands -
439   ---------------------*/
440
441- (PlaylistItem *) getCurrentSong
442{
443  struct mpd_status *mpdStatus;
444  PlaylistItem *currSong = nil;
445
446  if (! [self _checkConnection]) {
447      return currSong;
448  }
449
450  if (!mpd_command_list_begin(mpdConnection, true) ||
451    !mpd_send_status(mpdConnection) ||
452    !mpd_send_current_song(mpdConnection) ||
453    !mpd_command_list_end(mpdConnection)) {
454      return currSong;
455  }
456
457  mpdStatus = mpd_recv_status(mpdConnection);
458  if (mpdStatus == NULL) {
459    return currSong;
460  }
461
462  if(mpd_status_get_state(mpdStatus) == MPD_STATE_PLAY ||
463     mpd_status_get_state(mpdStatus) == MPD_STATE_PAUSE)
464    {
465	  struct mpd_song *mpdSong;
466
467	  if (!mpd_response_next(mpdConnection)) {
468	    return nil;
469	  }
470	  mpdSong = mpd_recv_song(mpdConnection);
471
472	  currSong = [[self _getPlaylistItemForSong: mpdSong] retain];
473
474	  [currSong setElapsedTime: mpd_status_get_elapsed_time(mpdStatus)];
475	  [currSong setTotalTime: mpd_status_get_total_time(mpdStatus)];
476	  mpd_song_free(mpdSong);
477
478      mpd_response_finish(mpdConnection);
479    }
480
481  mpd_status_free(mpdStatus);
482
483  if (currSong)
484    {
485      return [currSong autorelease];
486    }
487  else
488    {
489      return currSong;
490    }
491}
492
493- (int) getCurrentSongNr
494{
495  int songNr = -1;
496  struct mpd_status *mpdStatus;
497
498  if (! [self _checkConnection])
499    {
500      return songNr;
501    }
502
503  mpdStatus = mpd_run_status(mpdConnection);
504  if (mpdStatus != NULL) {
505    songNr = mpd_status_get_song_pos(mpdStatus)+1;
506
507    mpd_response_finish(mpdConnection);
508    mpd_status_free(mpdStatus);
509  }
510
511  return songNr;
512}
513
514- (int) getPlaylistLength
515{
516  int length = 0;
517  struct mpd_status *mpdStatus;
518
519  if (! [self _checkConnection]) {
520      return 0;
521  }
522
523  mpdStatus = mpd_run_status(mpdConnection);
524  if (mpdStatus != NULL) {
525    length = mpd_status_get_queue_length(mpdStatus);
526    mpd_response_finish(mpdConnection);
527    mpd_status_free(mpdStatus);
528  }
529
530  return length;
531}
532
533- (NSArray *) getPlaylist
534{
535  NSMutableArray *playlist;
536  struct mpd_status *mpdStatus;
537
538  if (! [self _checkConnection]) {
539      return nil;
540  }
541  if(!mpd_send_list_queue_meta(mpdConnection)) {
542    return nil;
543  }
544  struct mpd_song *mpdSong;
545  playlist = [[NSMutableArray alloc] init];
546
547  while((mpdSong = mpd_recv_song(mpdConnection)) != NULL) {
548	  PlaylistItem *tmpSong;
549
550	  tmpSong = [[self _getPlaylistItemForSong: mpdSong] retain];
551	  [playlist addObject: tmpSong];
552	  [tmpSong release];
553          mpd_song_free(mpdSong);
554  }
555
556  mpd_response_finish(mpdConnection);
557  mpdStatus = mpd_run_status(mpdConnection);
558  if (mpdStatus != NULL) {
559    currPlaylistVersion = mpd_status_get_queue_version(mpdStatus);
560    mpd_response_finish(mpdConnection);
561    mpd_status_free(mpdStatus);
562  } else {
563    currPlaylistVersion = 0;
564  }
565  return [playlist autorelease];
566}
567
568- (BOOL) collectionChanged
569{
570  BOOL retVal = NO;
571  unsigned int tmpArtistsNum = [self _getAllArtistsCount];
572  unsigned int tmpAlbumsNum = [self _getAllAlbumsCount];
573  if (tmpArtistsNum != numArtists) {
574    numArtists = tmpArtistsNum;
575    retVal = YES;
576  }
577  if (tmpAlbumsNum != numAlbums) {
578    numAlbums = tmpAlbumsNum;
579    retVal = YES;
580  }
581  return retVal;
582}
583
584- (BOOL) playlistChanged
585{
586
587  BOOL changed = NO;
588  struct mpd_status *mpdStatus;
589
590  if (! [self _checkConnection]) {
591      return NO;
592  }
593
594  mpdStatus = mpd_run_status(mpdConnection);
595  if (mpdStatus != NULL) {
596    unsigned int version = 0;
597    version = mpd_status_get_queue_version(mpdStatus);
598    mpd_response_finish(mpdConnection);
599    mpd_status_free(mpdStatus);
600    if (version != currPlaylistVersion) {
601      changed = YES;
602    } else {
603      changed = NO;
604    }
605  }
606  return changed;
607}
608
609- (void) shuffleList
610{
611  if (! [self _checkConnection]) {
612      return;
613  }
614  mpd_run_shuffle(mpdConnection);
615}
616
617- (void) clearPlaylist
618{
619  if (! [self _checkConnection]) {
620      return;
621  }
622  mpd_run_clear(mpdConnection);
623}
624
625- (void) removeSong: (int)song
626{
627  if (! [self _checkConnection]) {
628      return;
629  }
630  mpd_run_delete(mpdConnection, song);
631}
632
633- (void) removeSongRange: (NSRange)songRange
634{
635  if (! [self _checkConnection]) {
636      return;
637  }
638  if (songRange.location != NSNotFound)
639    {
640      mpd_run_delete_range(mpdConnection, songRange.location, songRange.length);
641    }
642}
643
644
645- (void) addTrack: (NSString *)file
646{
647  if (! [self _checkConnection]) {
648      return;
649  }
650  mpd_run_add(mpdConnection, [file UTF8String]);
651}
652
653- (void) moveSongNr: (int)song1 to: (int)song2
654{
655  if (! [self _checkConnection]) {
656      return;
657  }
658  mpd_run_move(mpdConnection, song1, song2);
659}
660
661- (void) moveSongWithID: (int)song1 to: (int)song2
662{
663  if (! [self _checkConnection]) {
664      return;
665  }
666  mpd_run_move_id(mpdConnection, song1, song2);
667}
668
669- (NSArray *) getAllPlaylists
670{
671  NSMutableArray *tmpArray;
672  struct mpd_entity *mpdInfoEntity;
673
674  if (! [self _checkConnection]) {
675      return nil;
676  }
677  mpd_send_list_playlists(mpdConnection);
678
679  tmpArray = [[NSMutableArray alloc] init];
680
681  while((mpdInfoEntity = mpd_recv_entity(mpdConnection))) {
682    if(mpd_entity_get_type(mpdInfoEntity) == MPD_ENTITY_TYPE_PLAYLIST) {
683      const struct mpd_playlist *mpdPlaylist = mpd_entity_get_playlist(mpdInfoEntity);
684      [tmpArray addObject: [NSString stringWithUTF8String: mpd_playlist_get_path(mpdPlaylist)]];
685    }
686    mpd_entity_free(mpdInfoEntity);
687  }
688  mpd_response_finish(mpdConnection);
689
690  return [tmpArray autorelease];
691
692}
693
694- (void) loadPlaylist: (NSString *)title
695{
696  if (! [self _checkConnection]) {
697      return;
698  }
699  mpd_run_load(mpdConnection, [title cString]);
700}
701
702- (void) savePlaylist: (NSString *)title
703{
704  if (! [self _checkConnection]) {
705      return;
706  }
707  mpd_run_save(mpdConnection, [title cString]);
708}
709
710- (void) removePlaylist: (NSString *)title
711{
712  if (! [self _checkConnection]) {
713      return;
714  }
715  mpd_run_rm(mpdConnection, [title cString]);
716}
717
718
719/* -----------------------
720   - Collection Commands -
721   -----------------------*/
722
723- (NSArray *) getAllArtists
724{
725  NSMutableArray *allArtists;
726  struct mpd_pair *pair;
727
728  if (! [self _checkConnection]) {
729      return nil;
730  }
731  mpd_search_db_tags(mpdConnection, MPD_TAG_ARTIST);
732  mpd_search_commit(mpdConnection);
733
734  allArtists = [[NSMutableArray alloc] init];
735
736  while ((pair = mpd_recv_pair_tag(mpdConnection, MPD_TAG_ARTIST)) != NULL)
737    {
738      [allArtists addObject: pair->value ?[NSString stringWithUTF8String: pair->value]:@""];
739      mpd_return_pair(mpdConnection, pair);
740    }
741
742  mpd_response_finish(mpdConnection);
743  return [[allArtists autorelease] sortedArrayUsingFunction: _stringSort
744		                       context: NULL];
745}
746
747- (NSArray *) getAllAlbums
748{
749  NSMutableArray *allAlbums;
750  struct mpd_pair *pair;
751
752  if (! [self _checkConnection]) {
753      return nil;
754  }
755  mpd_search_db_tags(mpdConnection, MPD_TAG_ALBUM);
756  mpd_search_commit(mpdConnection);
757
758  allAlbums = [[NSMutableArray alloc] init];
759
760  while ((pair = mpd_recv_pair_tag(mpdConnection, MPD_TAG_ALBUM)) != NULL) {
761      [allAlbums addObject: [NSString stringWithUTF8String: pair->value]];
762      free(pair);
763  }
764
765  mpd_response_finish(mpdConnection);
766  return [[allAlbums autorelease] sortedArrayUsingFunction: _stringSort
767	                              context: NULL];
768}
769
770- (NSArray *) getAllTracksWithMetadata: (BOOL) withMetadata
771{
772  NSMutableArray *allTracks;
773  struct mpd_entity *mpdEntity;
774
775  if (! [self _checkConnection])
776    {
777      return nil;
778    }
779
780  allTracks = [[NSMutableArray alloc] init];
781  if (withMetadata)
782    {
783      mpd_send_list_all_meta(mpdConnection, NULL);
784    }
785  else
786    {
787      mpd_send_list_all(mpdConnection, NULL);
788    }
789  while ((mpdEntity = mpd_recv_entity(mpdConnection)) != NULL) {
790    if (mpd_entity_get_type(mpdEntity) == MPD_ENTITY_TYPE_SONG) {
791          const struct mpd_song *mpdSong;
792          PlaylistItem *tmpSong;
793
794          mpdSong = mpd_entity_get_song(mpdEntity);
795          tmpSong = [[self _getPlaylistItemForSong: mpdSong] retain];
796          [allTracks addObject: tmpSong];
797	  [tmpSong release];
798    }
799    mpd_entity_free(mpdEntity);
800  }
801  mpd_response_finish(mpdConnection);
802  return [allTracks autorelease];
803}
804
805-(NSArray *) getAllFilesInDirectory: (NSString *) directory
806{
807  NSMutableArray *allFiles;
808  NSArray *allTracks;
809  NSEnumerator *trackEnum;
810  PlaylistItem *song;
811
812  allFiles = [[NSMutableArray alloc] init];
813  allTracks = [self getAllTracksWithMetadata: NO];
814  trackEnum = [allTracks objectEnumerator];
815
816  while((song = [trackEnum nextObject]) != nil)
817    {
818      NSString *songDir = [NSString stringWithFormat:@"%@", [[song getPath] stringByDeletingLastPathComponent]];
819      if ([directory isEqual:songDir])
820	{
821	  [allFiles addObject:[[song getPath] lastPathComponent]];
822	}
823    }
824  return [allFiles autorelease];
825}
826
827- (NSArray *) getAlbumsForArtist: (NSString *)artist
828{
829#warning FIXME getAllAlbumsForArtist
830return nil;
831/*
832  NSMutableArray *allAlbums;
833  char *albumName;
834
835  if (! [self _doConnect])
836    {
837      return nil;
838    }
839
840
841  mpd_sendListCommand(mpdConnection, MPD_TAG_ALBUM, [artist UTF8String]);
842
843  if (! [self _checkConnection])
844    {
845      return nil;
846    }
847
848  allAlbums = [[NSMutableArray alloc] init];
849
850  while ((albumName = mpd_getNextAlbum(mpdConnection)) != NULL)
851    {
852      [allAlbums addObject: [NSString stringWithUTF8String: albumName]];
853      free(albumName);
854    }
855
856  mpd_response_finish(mpdConnection);
857
858  return [[allAlbums autorelease] sortedArrayUsingFunction: _stringSort
859		                      context:NULL];
860*/
861
862}
863
864- (NSArray *) getAllTracksForArtist: (NSString *)artist
865{
866#warning FIXME getAllTracksForArtist
867
868return nil;
869
870/*
871  NSMutableArray *allTracks;
872  struct mpd_entity *mpdInfoEntity;
873
874  if (! [self _doConnect])
875    {
876      return nil;
877    }
878
879  mpd_sendFindCommand(mpdConnection, MPD_TAG_ARTIST, [artist UTF8String]);
880
881  if (! [self _checkConnection])
882    {
883      return nil;
884    }
885
886  allTracks = [[NSMutableArray alloc] init];
887
888  while((mpdInfoEntity = mpd_recv_entity(mpdConnection)))
889    {
890      if(mpd_entity_get_type(mpdInfoEntity) == MPD_ENTITY_TYPE_SONG)
891	{
892	  const struct mpd_song *mpdSong;
893	  PlaylistItem *tmpSong;
894
895	  mpdSong = mpd_entity_get_song(mpdInfoEntity);
896
897	  tmpSong = [[self _getPlaylistItemForSong: mpdSong] retain];
898
899	  [allTracks addObject: tmpSong];
900
901	  [tmpSong release];
902	}
903      mpd_entity_free(mpdInfoEntity);
904    }
905
906  mpd_response_finish(mpdConnection);
907
908  return [allTracks autorelease];
909
910*/
911}
912
913- (NSArray *) getAllTracksForArtist: (NSString *)artist
914                              album: (NSString *)album
915{
916  NSArray *tmpArray;
917  NSMutableArray *allTracks;
918  int i;
919
920  tmpArray = [[self getAllTracksForArtist: artist] retain];
921
922  if (! tmpArray) {
923      return nil;
924  }
925
926  allTracks = [[NSMutableArray alloc] init];
927
928  for (i = 0; i < [tmpArray count]; i++) {
929      if ([[[tmpArray objectAtIndex: i] getAlbum] isEqual: album]) {
930	  [allTracks addObject: [tmpArray objectAtIndex: i]];
931      }
932  }
933
934  [tmpArray release];
935  return [allTracks autorelease];
936}
937
938- (NSArray *) getAllTracksForAlbum: (NSString *)album
939{
940
941#warning FIXME getAllTracksForAlbum
942
943return nil;
944/*
945  NSMutableArray *allTracks;
946  struct mpd_entity *mpdInfoEntity;
947
948  if (! [self _doConnect])
949    {
950      return nil;
951    }
952
953  mpd_sendFindCommand(mpdConnection, MPD_TAG_ALBUM, [album UTF8String]);
954
955  if (! [self _checkConnection])
956    {
957      return nil;
958    }
959
960  allTracks = [[NSMutableArray alloc] init];
961
962  while((mpdInfoEntity = mpd_recv_entity(mpdConnection)))
963    {
964      if(mpd_entity_get_type(mpdInfoEntity) == MPD_ENTITY_TYPE_SONG)
965	{
966	  const struct mpd_song *mpdSong;
967	  PlaylistItem *tmpSong;
968
969	  mpdSong = mpd_entity_get_song(mpdInfoEntity);
970
971	  tmpSong = [[self _getPlaylistItemForSong: mpdSong] retain];
972
973	  [allTracks addObject: tmpSong];
974
975	  [tmpSong release];
976	}
977      mpd_entity_free(mpdInfoEntity);
978    }
979
980  mpd_response_finish(mpdConnection);
981
982  return [allTracks autorelease];
983*/
984}
985
986- (void) updateCollection
987{
988  if (! [self _checkConnection]) {
989      return;
990  }
991
992  mpd_send_update(mpdConnection, "");
993  mpd_response_finish(mpdConnection);
994  if ([self collectionChanged])
995    {
996      NSNotification *aNotif;
997      aNotif = [NSNotification notificationWithName:
998                ShownCollectionChangedNotification
999                object: nil];
1000      [[NSNotificationCenter defaultCenter]
1001                postNotification: aNotif];
1002    }
1003}
1004
1005- (NSArray *) getAllDirectories
1006{
1007  NSMutableArray *allDirs;
1008  struct mpd_directory *dir;
1009
1010  if (! [self _checkConnection])
1011    {
1012      return nil;
1013    }
1014
1015  allDirs = [[NSMutableArray alloc] init];
1016  mpd_send_list_all(mpdConnection, NULL);
1017  while ((dir = mpd_recv_directory(mpdConnection)) != NULL)
1018    {
1019      const char *tmpdir;
1020      tmpdir = mpd_directory_get_path(dir);
1021
1022      [allDirs addObject:[NSString stringWithUTF8String:tmpdir]];
1023      mpd_directory_free(dir);
1024    }
1025  mpd_response_finish(mpdConnection);
1026
1027  return [allDirs autorelease];
1028}
1029
1030@end
1031
1032/* -------------------
1033   - Private Methods -
1034   -------------------*/
1035
1036@implementation MPDController(Private)
1037- (BOOL) _doConnect
1038{
1039  char *mpdHost;
1040  int mpdPort;
1041  unsigned int mpdTimeout;
1042
1043  mpdHost = (char *)[host cString];
1044  mpdPort = (int) [port floatValue];
1045  mpdTimeout = (unsigned int) [timeout intValue];
1046  mpdConnection = mpd_connection_new(mpdHost, mpdPort, mpdTimeout);
1047  if (mpdConnection == NULL) {
1048    NSLog(@"Cannot connect to server: Out of memory");
1049    return NO;
1050  }
1051
1052  if (password) {
1053    mpd_send_password(mpdConnection, (char *)[password cString]);
1054    mpd_response_finish(mpdConnection);
1055  }
1056
1057  if (mpd_connection_get_error(mpdConnection) != MPD_ERROR_SUCCESS) {
1058    [[NSNotificationCenter defaultCenter] postNotification:
1059	     [NSNotification notificationWithName: DidNotConnectNotification
1060					   object: [NSString stringWithUTF8String:
1061					       mpd_connection_get_error_message(mpdConnection)]]];
1062    mpd_connection_clear_error(mpdConnection);
1063    return NO;
1064  } else {
1065    [[NSNotificationCenter defaultCenter] postNotification:
1066	     [NSNotification notificationWithName: DidConnectNotification object: nil]];
1067    return YES;
1068  }
1069}
1070
1071- (BOOL) _checkConnection
1072{
1073  if (mpd_connection_get_error(mpdConnection) != MPD_ERROR_SUCCESS) {
1074    if (mpd_connection_get_error(mpdConnection) == MPD_ERROR_TIMEOUT) {
1075      // we are trying to reconnect here
1076      // _doConnect will send the notification if it also fails
1077      mpd_response_finish(mpdConnection);
1078      mpd_connection_clear_error(mpdConnection);
1079      mpd_connection_free(mpdConnection);
1080      if ([self _doConnect]) {
1081        return YES;
1082      }
1083    } else {
1084      mpd_response_finish(mpdConnection);
1085      [[NSNotificationCenter defaultCenter] postNotification:
1086	     [NSNotification notificationWithName: DidNotConnectNotification
1087			                   object: [NSString stringWithUTF8String:
1088							       mpd_connection_get_error_message(mpdConnection)]]];
1089      mpd_connection_clear_error(mpdConnection);
1090      mpd_response_finish(mpdConnection);
1091    }
1092    return NO;
1093  } else {
1094    mpd_response_finish(mpdConnection);
1095    return YES;
1096  }
1097}
1098
1099- (PlaylistItem *) _getPlaylistItemForSong: (const struct mpd_song *)anSong
1100{
1101  PlaylistItem *plItem;
1102  const char *songArtist = mpd_song_get_tag(anSong,MPD_TAG_ARTIST,0);
1103  const char *songTitle = mpd_song_get_tag(anSong,MPD_TAG_TITLE,0);
1104  const char *songAlbum = mpd_song_get_tag(anSong,MPD_TAG_ALBUM,0);
1105  const char *songTrackNr = mpd_song_get_tag(anSong,MPD_TAG_TRACK,0);
1106  const char *songGenre = mpd_song_get_tag(anSong,MPD_TAG_GENRE,0);
1107  const char *songDate = mpd_song_get_tag(anSong,MPD_TAG_DATE,0);
1108  const char *songComposer = mpd_song_get_tag(anSong,MPD_TAG_COMPOSER,0);
1109  const char *songPerformer = mpd_song_get_tag(anSong,MPD_TAG_PERFORMER,0);
1110  const char *songDisc = mpd_song_get_tag(anSong,MPD_TAG_DISC,0);
1111  const char *songComment = mpd_song_get_tag(anSong,MPD_TAG_COMMENT,0);
1112
1113  plItem = [[PlaylistItem alloc] init];
1114
1115  if (songArtist == NULL) {
1116      [plItem setArtist: _(@"Unknown Artist")];
1117  } else {
1118      [plItem setArtist: [NSString stringWithUTF8String: songArtist]];
1119  }
1120
1121  if (songTitle == NULL) {
1122      [plItem setTitle: _(@"Unknown Title")];
1123  } else {
1124      [plItem setTitle: [NSString stringWithUTF8String: songTitle]];
1125  }
1126
1127  if (songAlbum == NULL) {
1128      [plItem setAlbum: _(@"Unknown Album")];
1129  } else {
1130      [plItem setAlbum: [NSString stringWithUTF8String: songAlbum]];
1131  }
1132
1133  if (songGenre == NULL) {
1134      [plItem setGenre: _(@"Unknown Genre")];
1135  } else {
1136      [plItem setGenre: [NSString stringWithUTF8String: songGenre]];
1137  }
1138
1139  if (songDate == NULL) {
1140      [plItem setDate: _(@"Unknown")];
1141  } else {
1142      [plItem setDate: [NSString stringWithUTF8String: songDate]];
1143  }
1144
1145  if (songComposer == NULL) {
1146      [plItem setComposer: _(@"No composer info available")];
1147  } else {
1148      [plItem setComposer: [NSString stringWithUTF8String: songComposer]];
1149  }
1150
1151  if (songPerformer == NULL) {
1152      [plItem setPerformer: _(@"No performer info available")];
1153  } else {
1154      [plItem setPerformer: [NSString stringWithUTF8String: songPerformer]];
1155  }
1156
1157  if (songDisc == NULL) {
1158      // if we don't know, we just assume its the first disc
1159      [plItem setDisc: _(@"1")];
1160  } else {
1161      [plItem setDisc: [NSString stringWithUTF8String: songDisc]];
1162  }
1163
1164  if (songComment == NULL) {
1165      [plItem setComment: _(@"No Comment")];
1166  } else {
1167      [plItem setComment: [NSString stringWithUTF8String: songComment]];
1168  }
1169
1170  [plItem setPath: [NSString stringWithUTF8String: mpd_song_get_uri(anSong)]];
1171
1172  if (songTrackNr == NULL) {
1173      [plItem setTrackNr: @""];
1174  } else {
1175      [plItem setTrackNr: [NSString stringWithUTF8String: songTrackNr]];
1176  }
1177
1178  [plItem setTotalTime: mpd_song_get_duration(anSong)];
1179
1180  [plItem setID: mpd_song_get_id(anSong)];
1181  [plItem setPos: mpd_song_get_pos(anSong)];
1182  return [plItem autorelease];
1183}
1184
1185int _stringSort(id string1, id string2, void *context)
1186{
1187  NSString *str1, *str2;
1188
1189  str1 = (NSString *) string1;
1190  str2 = (NSString *) string2;
1191
1192  return [str1 caseInsensitiveCompare: str2];
1193}
1194
1195- (unsigned int) _getAllArtistsCount
1196{
1197 struct mpd_stats *mpdStats;
1198 unsigned int artists = 0;
1199 if (! [self _checkConnection]) {
1200      return artists;
1201  }
1202  mpdStats = mpd_run_stats(mpdConnection);
1203  if (mpdStats != NULL) {
1204    artists = mpd_stats_get_number_of_artists(mpdStats);
1205    mpd_stats_free(mpdStats);
1206    mpd_response_finish(mpdConnection);
1207    return artists;
1208  } else {
1209    return artists;
1210  }
1211}
1212
1213- (unsigned int) _getAllAlbumsCount
1214{
1215 struct mpd_stats *mpdStats;
1216 unsigned int albums = 0;
1217 if (! [self _checkConnection]) {
1218      return albums;
1219  }
1220  mpdStats = mpd_run_stats(mpdConnection);
1221  if (mpdStats != NULL) {
1222    albums = mpd_stats_get_number_of_albums(mpdStats);
1223    mpd_stats_free(mpdStats);
1224    mpd_response_finish(mpdConnection);
1225    return albums;
1226  }
1227  return albums;
1228}
1229
1230@end
1231