#include "stringa.h"

// La macro test serve in fase di debug
#ifdef _DEBUG
#define test(msg,ogg) cout << "*********"msg": \"" << ogg.prappr->s << \
"\" aggangiata da " << ogg.prappr->conta << " oggetti\n";
#endif

namespace spazio_stringa
{
// Alcune funzioni inline usate solo dall'implementazione di stringa ....
	inline void controlla(int dim, int ind)
	{
		if ( ind < 0 || ind >= dim ) throw Errore();
	}

// Le funzioni confronta, lunghezza e copia operano su stringhe "in stile
// C" (non si č voluto ricorrere alle funzioni di libreria per rendere
// questo modulo indipendente dalle librerie del C)
	inline bool confronta(const char* str1, const char* str2)
	{
		while( *str1 && *str1 == *str2 ) str1++,str2++;
		return !*str1 && !*str2;
	}

	inline int lunghezza(const char* str)
	{
		int len = 0;
		while(*(str+len)) len++;
		return len;
	}

	inline void copia(const char* inpstr, char* outstr)
	{
		while (*outstr++ = *inpstr++);
	}

	struct stringa::rappr
	{
		char* s;
		int dim;
		int conta;
		rappr() : conta(1) { }
		rappr(const char* str) : conta(1), dim(lunghezza(str))
		{
			s = new char [dim+1]; // una posizione in pių per il terminator
			copia(str,s);
		}
		~rappr() { delete [] s; }
		rappr* clona()
		{
			if ( conta == 1 ) return this;
			conta--;
			return new rappr(s); // copia se stesso
		}
	};

	stringa::stringa()
	{
		prappr = new rappr("");
	}

	stringa::stringa(const char* str)
	{
		prappr = new rappr(str);
	}

	stringa::stringa(const stringa& str)
	{
		str.prappr->conta++;
		prappr = str.prappr;  // condivide la rappresentazione di str
#ifdef _DEBUG
test("Costruttore di copia",str)
#endif
	}

	stringa& stringa::operator=(const stringa& str)
	{
		str.prappr->conta++;   // prima questa istuzione, per prottegersi 
//                                dalle auto-assegnazioni 
		if ( --prappr->conta == 0 ) delete prappr; //
		prappr = str.prappr;   // condivide la rappresentazione
#ifdef _DEBUG
test("Oper. di assegnazione",str)
#endif
		return *this;
	}

	stringa::~stringa()
	{
#ifdef _DEBUG
test("Distruttore",(*this))
#endif
		if ( --prappr->conta == 0 ) delete prappr;
	}

	char stringa::estrai(int i) const
	{
#ifdef _DEBUG
test("Estrazione",(*this))
#endif
		return  prappr->s[i];
	}

	void stringa::inserisci(int i, char c)
	{
		prappr = prappr->clona();
		prappr->s[i] = c;
#ifdef _DEBUG
test("Inserimento",(*this))
#endif
	}

	int stringa::dimensione() const
	{
		return prappr->dim;
	}

	char stringa::operator[](int i) const
	{
		controlla(dimensione(),i);
		return estrai(i);
	}

	stringa::rifc stringa::operator[](int i)
	{
		controlla(dimensione(),i);
		return rifc(*this,i);
	}

	void stringa::ricrea(char* s, int d)
	{
		if ( --prappr->conta == 0 ) delete prappr;
		prappr = new rappr();
		prappr->s = s;
		prappr->dim = d;
	}

	void stringa::ridimensiona(int d)
	{
		int dim = dimensione();
		if ( d == dim ) return;
		char* s = new char [d+1];
		int i = 0;
		while ( i < dim && i < d ) s[i++] = prappr->s[i];
		while ( i < d ) s[i++] = ' ';
		s[d] = '\0';
		ricrea(s,d);
	}

	stringa& stringa::operator+=(const stringa& str)
	{
		int dim = dimensione();
		int d = dim+str.dimensione();
		ridimensiona(d);
		for (int i=dim; i < d; i++) prappr->s[i] = str.prappr->s[i-dim];
		return *this;
	}

	stringa operator+(const stringa& str1, const stringa& str2)
	{
		stringa temp = str1;
		temp += str2;
		return temp;
	}

	bool operator==(const stringa& str1, const stringa& str2)
	{
		return confronta(str1.prappr->s,str2.prappr->s);
	}

	bool operator!=(const stringa& str1, const stringa& str2)
	{
		return !(str1==str2);
	}

	ostream& operator<<(ostream& out, const stringa& str)
	{
		out << str.prappr->s;
		return out;
	}

	istream& operator>>(istream& inp, stringa& str)
	{
// Per analogia con l'overload di operator>> per le stringhe "in stile C",
// uso i blanks e gli altri caratteri speciali come separatori fra stringhe
// Procedo con allocazioni successive ad incrementi di 10 finchč non ho
// memorizzato tutta la stringa
		const int incremento = 10;
		const char separatore = ' ';
		char ch;
		int dim = incremento;
		char* s = new char [dim+1];
		str.ricrea(s,dim);
		int numc = 0;
		inp >> ch;
		do 
		{
			if ( numc >= dim )
			{
				dim += incremento;
				str.ridimensiona(dim);
			}
			str.prappr->s[numc++] = ch;
			cin.get(ch);
		} while ( ch > separatore );
		str.prappr->s[numc] = '\0';
		if ( numc < dim ) str.ridimensiona(numc);
		return inp;
	}

}


