// -*- c++ -*-
//------------------------------------------------------------------------------
// CardDeck.cpp
//------------------------------------------------------------------------------
// $Id: CardDeck.cpp,v 1.32 2008/08/04 02:54:31 vlg Exp $
//
// Copyright (c) 2004,2006 by Vladislav Grinchenko
//
// 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.
//
// Date: Apr 24 2004
//------------------------------------------------------------------------------
#include "Granule.h"
#include "CardDeck.h"
#include "Granule-main.h"
#include "Intern.h"
#include <sstream>
#include <fstream>
#include <algorithm>
#include <glib.h>
void
CardDeck::
set_name (int idx_)
{
std::ostringstream os;
os << "Card Box " << idx_;
m_cdname = os.str ();
m_index = idx_;
}
bool
CardDeck::
erase (VCard* vcard_)
{
trace_with_mask("CardDeck::erase(VCard)",GUITRACE|DECK);
cardlist_iterator p = m_cards.begin ();
while (p != m_cards.end ())
{
if ((*p) == vcard_) {
erase (p);
return true;
}
p++;
}
return false;
}
void
CardDeck::
play_card (SideSelection side_, VDeck::cardlist_iterator& iter_)
{
CardRef* cref = dynamic_cast<CardRef*> (*iter_);
Assure_exit (cref);
if (!Deck::audio_playback_active ()) {
cref->get_deck ().play_card (side_, iter_);
}
else {
DL ((GRAPP,"Already playing an audio clip.\n"));
}
}
void
CardDeck::
remove_duplicates ()
{
trace_with_mask("CardDeck::remove_duplicates",GUITRACE);
CardRef* cr_p;
CardRef* cr_q;
cardlist_iterator p;
cardlist_iterator q;
DL ((GRAPP,"cards before = %d\n", m_cards.size ()));
std::sort (m_cards.begin (), m_cards.end (), Compare_CardRefs ());
p = m_cards.begin ();
if (p == m_cards.end ()) {
goto done;
}
q = p + 1;
while (q != m_cards.end ())
{
cr_p = dynamic_cast<CardRef*> (*p);
cr_q = dynamic_cast<CardRef*> (*q);
Assure_exit (cr_p && cr_q);
if (cr_p->get_card().get_id() == cr_q->get_card().get_id() &&
cr_p->get_reversed() == cr_q->get_reversed ())
{
delete (*q);
m_cards.erase (q);
q = p;
}
else {
p = q;
}
q++;
}
done:
DL ((GRAPP,"cards after = %d\n", m_cards.size ()));
}
/**
* Report the ratio of cards already reviewed vs. total and percentage-wise.
*
* The size of the deck is a constant determined at the moment when
* DeckPlayer starts playing it. It can change when a card is moved
* to the upper or lower box. It can also remain constant when
* the card is sent to the end of the deck for repetition (fringe
* cases). The important concept here is that we want to show the user
* how many cards he has seen so far and how many remains to be seen
* at least once.
*
* We, therefore, consider initial Deck size and count our advance
* through the Deck (first sweep) ourselves.
*/
void
CardDeck::
get_progress (float& pct_done_, std::string& msg_, int /* idx_ */)
{
trace_with_mask("CardDeck::get_progress",GUITRACE);
std::ostringstream os;
DL((GRAPP,"m_init_size = %d; m_progress_idx = %d\n",
m_init_size, m_progress_idx));
if (m_init_size == 0) {
pct_done_ = 0;
msg_ = "";
return;
}
if (m_progress_idx == m_init_size) {
pct_done_ = 1.0;
os << m_progress_idx << "/" << m_init_size;
return;
}
m_progress_idx++;
os << (m_progress_idx) << "/" << m_init_size;
msg_ = os.str ();
if (m_cards.size () <= 1) {
pct_done_ = 0;
}
else {
pct_done_ = (m_progress_idx) * 1.0 / m_init_size;
}
}
void
CardDeck::
reset_progress ()
{
trace_with_mask("CardDeck::reset_progress",GUITRACE|DECK);
m_init_size = size ();
m_progress_idx = 0;
}
void
CardDeck::
clear ()
{
trace_with_mask("CardDeck::clear",GUITRACE);
cardlist_iterator p = m_cards.begin ();
while (p != m_cards.end ()) {
delete (*p);
p++;
}
m_cards.clear ();
}
void
CardDeck::
shuffle_subsets ()
{
trace_with_mask("CardDeck::shuffle_subsets",GUITRACE);
if (m_cards.size () <= 1) {
return;
}
/** Algorithm
*/
bool inset = false; // are we inside the subset?
cardlist_iterator first;
cardlist_iterator second;
first = second = m_cards.begin ();
while (second + 1 != m_cards.end ()) {
second++;
if ((**first) == (**second)) {
inset = true;
continue;
}
else {
if (inset) {
random_shuffle (first, second);
inset = false;
}
first = second;
}
}
if (inset) {
random_shuffle (first, second+1);
}
}
void
CardDeck::
dump_to_file ()
{
VCard* vcard;
std::ostringstream fname;
fname << "/tmp/cardbox" << get_index () << ".dat";
::unlink (fname.str ().c_str ());
std::ofstream fdump (fname.str ().c_str ());
if (!fdump) {
DL ((DECK,"Failed to open dump file \"%s\"\n", fname.str ().c_str ()));
return;
}
DL ((DECK,"Dumping to file \"%s\"\n", fname.str ().c_str ()));
cardlist_const_iterator iter = begin ();
while (iter != end ()) {
vcard = *iter;
fdump << Granule::locale_from_utf8 (
Granule::trim_multiline (vcard->get_question ()));
fdump << " ";
fdump << vcard->get_expiration ().fmtString ().c_str ();
fdump << '\n';
iter++;
}
fdump.close ();
}
/** For every card in the file, create a subdirectory
with the name of the first letter of the word in the card
and put the word in it.
*/
void
CardDeck::
export_sound_bits (const std::string& path_)
{
trace_with_mask("CardDeck::export_sound_bits",GUITRACE);
#if !defined (IS_WIN32)
string word;
string to;
string from;
string from_path;
vector<string> v;
from_path = Granule::filename_to_utf8 (CONFIG->get_snd_archive_path ());
from_path += G_DIR_SEPARATOR_S;
cardlist_const_iterator citer = m_cards.begin ();
while (citer != m_cards.end ()) {
word = Granule::locale_from_utf8 ((*citer)->get_question ());
ASSA::Utils::split (word.c_str (), v);
if (v.size () > 1) {
word = v[0] != "to" ? v[0] : v[1];
}
from = from_path + word[0] + G_DIR_SEPARATOR_S + word + ".wav";
if (!g_file_test (from.c_str (), G_FILE_TEST_IS_REGULAR)) {
citer++;
continue;
}
to = path_ + G_DIR_SEPARATOR_S + word[0];
if (!g_file_test (to.c_str (), G_FILE_TEST_IS_DIR))
{
#if defined (IS_WIN32)
::mkdir (to.c_str ());
#else
::mkdir (to.c_str (), 0755);
#endif
}
to += G_DIR_SEPARATOR_S + word + ".wav";
/** Uber-fast file copy
*/
std::ifstream ifs (from.c_str (), std::ios::in | std::ios::binary);
std::ofstream ofs (to.c_str (), std::ios::out | std::ios::binary);
ofs << ifs.rdbuf();
ifs.close ();
ofs.close ();
citer++;
}
#endif // !def IS_WIN32
}