package eu.dnetlib.functionality.recommendation.app;

import eu.dnetlib.api.data.SearchService;
import eu.dnetlib.api.data.SearchServiceException;
import eu.dnetlib.api.functionality.CommunityService;
import eu.dnetlib.api.functionality.RecommendationService;
import eu.dnetlib.api.functionality.RecommendationServiceException;
import eu.dnetlib.api.functionality.UserProfileService;
import eu.dnetlib.api.functionality.UserProfileServiceException;
import eu.dnetlib.domain.ActionType;
import eu.dnetlib.domain.EPR;
import eu.dnetlib.domain.ResourceType;
import eu.dnetlib.domain.functionality.QueryHash;
import eu.dnetlib.domain.functionality.QueryHashSearchCriteria;
import eu.dnetlib.domain.functionality.Recommendation;
import eu.dnetlib.domain.functionality.RecommendationComparator;
import eu.dnetlib.domain.functionality.RecommendationSearchCriteria;
import eu.dnetlib.domain.functionality.SavedQuery;
import eu.dnetlib.domain.functionality.UserProfile;
import eu.dnetlib.domain.functionality.UserProfileSearchCriteria;
import eu.dnetlib.functionality.recommendation.dao.QueryHashDAO;
import eu.dnetlib.functionality.recommendation.dao.RecommendationDAO;
import eu.dnetlib.functionality.recommendation.domain.Document;
import eu.dnetlib.functionality.recommendation.parser.DriverDocumentHandler;
import eu.dnetlib.utils.md5.MD5;
import gr.uoa.di.driver.app.DriverServiceImpl;
import gr.uoa.di.driver.dao.DAOException;
import gr.uoa.di.driver.enabling.ISLookUp;
import gr.uoa.di.driver.enabling.ISLookUpException;
import gr.uoa.di.driver.enabling.resultset.ResultSet;
import gr.uoa.di.driver.enabling.resultset.ResultSetFactory;
import gr.uoa.di.driver.util.ServiceLocator;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.xml.bind.JAXBException;

import org.apache.log4j.Logger;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.sun.org.apache.xerces.internal.parsers.SAXParser;


public class RecommendationServiceImpl extends DriverServiceImpl implements
        RecommendationService {

    public static String QUERY_UPDATE_MSG = "The DRIVER Alerting service is pleased to announce you that there are some new results for you stored queries.\n Have a look at driver-infrastructures.eu ...";
    public static String ANNOUNCEMENT_MAIL_SUBJECT = "Driver: Stored queries announcement";
    public static int ANNOUNCEMENT_DAYS_TO_EXPIRE = 10;
    // private CommunityNotificationManager notifier;
//    private boolean queryRunning;
    private MailHostPreferences preferences = new MailHostPreferences();
    private int sleepTime = 3600000;
    private String mailHost;
    private String mailPort;
    private boolean sendMail = true;
    private boolean running = false;
    private RecommendationDAO dao = null;
    private QueryHashDAO queryHashDao;
    private ServiceLocator<SearchService> searchServiceLocator = null;
    private ServiceLocator<UserProfileService> profileServiceLocator = null;
    private ServiceLocator<CommunityService> communityServiceLocator = null;
    private QueryRunner queryRunner;
    static Logger logger = Logger.getLogger( RecommendationServiceImpl.class );
    private ISLookUp<UserProfile> userProfilesLookup;
    private ISLookUp<QueryHash> queryHashLookup;
    private ResultSetFactory rsFactory = null;
    private int maxDocumentsForHashing = 20;
    private int mailPeriod = 7;
    private String savedQueryMailSub = "Saved query mail";
    private String savedQueryMailMsg = "mail";
    
    private CommunityNotificationManager communityNotificationManager = null;


    public RecommendationServiceImpl() {
    }

    @Override
    public void init() {
        logger.debug( "Init service" );
        super.init();
//        initQueryRunner();
//
        this.subscribe(ActionType.UPDATE, ResourceType.COMMUNITYDSRESOURCETYPE, this.communityNotificationManager );
        this.subscribe(ActionType.DELETE, ResourceType.COMMUNITYDSRESOURCETYPE, this.communityNotificationManager);
    }

    public void initQueryRunner() {
        logger.debug( "Making search thread" );
        queryRunner = new QueryRunner( this );
        logger.debug( "starting thread" );
        queryRunner.start();
        running = false;
    }

    public synchronized void initSearchQueryCheckOut() {
        logger.debug( "Search process just started" );
        if ( this.isRunning() ) {
            logger.debug( "isRunning is false, no search will be done" );
            return;
        }
        logger.debug( "isRunning is true, search will be performed" );
        this.setRunning( true );

        while ( true ) {
            try {
                try {
                    UserProfileSearchCriteria uSearchCriteria = new UserProfileSearchCriteria();
                    EPR epr = getUserProfilesLookup().search( uSearchCriteria );
                    ResultSet<UserProfile> rs = rsFactory.createResultSet( epr, UserProfile.class );
                    logger.debug( "I will scan " + rs.size() + " users" );
                    for ( int i = 1; i <= rs.size(); i++ ) {
                        List<UserProfile> profiles = rs.get( i, i + 10 );
                        for ( UserProfile profile : profiles ) {
                            runsUserQueries( profile );
                        }
                    }
                } catch ( Exception ex ) {
                    logger.error( ex );
                    ex.printStackTrace();
                }
                logger.debug( "I will sleep for " + sleepTime + " milliseconds " );
                logger.debug( "sleep Date = " + new Date() );
                Thread.sleep( this.sleepTime );
                if( this.isRunning() == false ) {
                  break;
                }
            } catch ( InterruptedException ex ) {
                java.util.logging.Logger.getLogger( RecommendationServiceImpl.class.getName() ).log( Level.SEVERE, null, ex );
            }
        }

        this.setRunning( false );
    }

    private void runsUserQueries( UserProfile profile ) throws DAOException, SearchServiceException, SAXException, IOException, ISLookUpException, ISLookUpException, NoSuchAlgorithmException, JAXBException, MessagingException {
        List<SavedQuery> queries = profile.getSavedQueries();        
        //System.out.println( "User " + profile.getEmail() + " has " + queries.size() + " queries " );
        if( getDaysSinceLastMail( profile.getResourceId() ) > this.mailPeriod ) {

        }
        StringBuffer mailContent = new StringBuffer();
        mailContent.append( this.savedQueryMailMsg );
        for ( SavedQuery query : queries ) {
            //System.out.println( "Search for user " + profile.getEmail() + " and query " + query.getCqlText() );
            QueryHash queryHash = getQueryHash( profile.getResourceId(), query.getCqlText() );
            if ( queryHash == null ) {
                queryHash = new QueryHash();
                queryHash.setUserId( profile.getResourceId() );
                queryHash.setCqlQuery( query.getCqlText() );
                setQueryHashValue( query, queryHash, new StringBuffer() );
                logger.debug( "saving new query hash" );
                queryHash = this.queryHashDao.save( queryHash );
                logger.debug( "new query hash saved with the id " + queryHash.getResourceId() );                
            } else {
                logger.debug( "Existing query hash for " + profile.getEmail() + " and cql " + query.getCqlText() + " is has id " + queryHash.getResourceId() );
                StringBuffer newMailContent = new StringBuffer();
                int oldHashValue = setQueryHashValue( query, queryHash, newMailContent );
                if( oldHashValue == queryHash.getHashValue() ) {
                    continue;
                }
                mailContent.append( newMailContent.toString() );
                QueryHash updatedQueryHash = new QueryHash();
                updatedQueryHash.setUserId( queryHash.getUserId() );
                updatedQueryHash.setCqlQuery( queryHash.getCqlQuery() );
                updatedQueryHash.setQueryResults( queryHash.getQueryResults() );
                this.queryHashDao.delete( queryHash );
                updatedQueryHash = this.queryHashDao.save( updatedQueryHash );
            }
        }
        if( profile.getRecommendationSendEmail() == true  ) {
            this.sendMail( profile.getEmail(), mailContent.toString(), this.savedQueryMailSub );
        }
    }

    private int setQueryHashValue( SavedQuery query, QueryHash qhash, StringBuffer mailMsg ) throws SearchServiceException, SAXException, IOException, NoSuchAlgorithmException {
        int oldHashValue = qhash.getHashValue();
        SearchService searchService = this.searchServiceLocator.getService();
        EPR epr = searchService.search( query.getCqlText() );
        ResultSet<String> rs = rsFactory.createResultSet( epr );
        int hash = 0;
        Hashtable<String, Boolean> ids = new Hashtable<String, Boolean>();
        List<String> docXMLs = rs.getElements( 1, maxDocumentsForHashing );
        mailMsg.append( "Saved query : " + query.getCqlText() + "\n" );
        mailMsg.append( "Reqults\n" );
        for ( String docXml : docXMLs ) {
            Document doc = getDocumentFromXML( docXml );
            if ( qhash.getQueryResults().get( doc.getId() ) == null ) {
                ids.put( doc.getId(), true );
            } else {
                ids.put( doc.getId(), false );
            }
            mailMsg.append( "Document : " + doc.getTitle() + " Author :" + doc.getAuthor() + "\n");
            qhash.setQueryResults( ids );
            hash += MD5.encrypt( doc.getTitle() );
        }
        mailMsg.append( "\n" );
        qhash.setHashValue( hash );
        return oldHashValue;
    }

    Document getDocumentFromXML( String xml ) throws SAXException, IOException {
        DriverDocumentHandler docHdl = new DriverDocumentHandler();

        docHdl.setIdField( "objIdentifier" );
        docHdl.setAuthorField( "creator" );
        docHdl.setTitleField( "title" );

        SAXParser parser = new SAXParser();

        parser.setContentHandler( docHdl );
        parser.parse( new InputSource( new ByteArrayInputStream( xml.getBytes() ) ) );

        return docHdl.getDocument();
    }

    private QueryHash getQueryHash( String uid, String cql ) throws DAOException {
        QueryHashSearchCriteria qsc = new QueryHashSearchCriteria();
        qsc.setCqlQuery( cql );
        qsc.setUserId( uid );
        logger.debug( "searching for query hash for " + uid + " query " + cql );
        List<QueryHash> qh = this.queryHashDao.search( qsc );
        logger.debug( "loading results for query hash for " + uid + " query " + cql );
        if ( qh.size() > 0 ) {
            return qh.get( 0 );
        } else {
            return null;
        }
    }

    private int getDaysSinceLastMail( String userId ) throws DAOException {
        logger.debug( "reading the queryHash profiles to get the last mail that send to user" );
        QueryHashSearchCriteria qsc = new QueryHashSearchCriteria();
        qsc.setUserId( userId );
        List<QueryHash> qhs = this.queryHashDao.search( qsc );
        Date oldestDate = new Date();
        for ( QueryHash qh : qhs ) {
            if( qh.getDateOfCreation().before( oldestDate ) ) {
                oldestDate.setTime( qh.getDateOfCreation().getTime() );
            }
        }
        return (int)( new Date().getTime() - oldestDate.getTime() ) / (1000 * 60 * 60 * 24);

    }

    public synchronized void setRunning( boolean running ) {
        this.running = running;
    }

    public boolean isRunning() {
        return running;
    }

    public ServiceLocator<SearchService> getSearchServiceLocator() {
        return searchServiceLocator;
    }

    public void setSearchServiceLocator(
            ServiceLocator<SearchService> searchServiceLocator ) {
        this.searchServiceLocator = searchServiceLocator;
    }

    public ServiceLocator<UserProfileService> getProfileServiceLocator() {
        return profileServiceLocator;
    }

    public void setCommunityServiceLocator(
            ServiceLocator<CommunityService> communityServiceLocator ) {
        this.communityServiceLocator = communityServiceLocator;
    }

    public ServiceLocator<CommunityService> getCommunityServiceLocator() {
        return this.communityServiceLocator;
    }

    public void setProfileServiceLocator(
            ServiceLocator<UserProfileService> profileServiceLocator ) {
        this.profileServiceLocator = profileServiceLocator;
    }

    public boolean isSendMail() {
        return sendMail;
    }

    public void setSendMail( boolean sendMail ) {
        this.sendMail = sendMail;
    }

    public String getMailHost() {
        return mailHost;
    }

    public void setMailHost( String mailHost ) {
        this.mailHost = mailHost;
    }

    public String getMailPort() {
        return mailPort;
    }

    public void setMailPort( String mailPort ) {
        this.mailPort = mailPort;
    }

    public int getSleepTime() {
        return sleepTime;
    }

    public void setSleepTime( int sleepTime ) {
        this.sleepTime = sleepTime;
    }

    public void setDao( RecommendationDAO dao ) {
        this.dao = dao;
    }

    @Override
    public void addRecommendationToUser( String recommendationId, String userId )
            throws RecommendationServiceException {
        Recommendation recommendation = null;
        try {
            recommendation = this.dao.getById( recommendationId );
            logger.debug( recommendation );
            logger.debug( recommendation.getResourceId() );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        recommendation.getUserIds().add( userId );
        try {
            dao.save( recommendation );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
    }

    @Override
    public String generateAnnouncement( int index, boolean active,
            String title, String announcementText,
            Date creationDate, Date expirationDate )
            throws RecommendationServiceException {
        logger.debug( "creating announcement" );
        Recommendation announcement = null;
        announcement = this.generateRecommendation( index, active,
                title,
                announcementText, creationDate, expirationDate );
        announcement.setType( Recommendation.ANOUCHMENT_TYPE );
        try {
            this.dao.save( announcement );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        return announcement.getResourceId();
    }

    @Override
    public String generateCommunityRecommendation( int index, boolean active,
            String title, String recommendationText,
            Date creationDate, Date expirationDate, Set<String> communityIds )
            throws RecommendationServiceException {
        logger.debug( "creating community recommendation" );
        Recommendation communityRecommendation = null;
        communityRecommendation = this.generateRecommendation( index,
                active,
                title,
                recommendationText,
                expirationDate, expirationDate );
        communityRecommendation.setType( Recommendation.COMMUNITY_TYPE );
        communityRecommendation.setCommunityIds( communityIds );

        try {
            this.dao.save( communityRecommendation );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }

        return communityRecommendation.getResourceId();
    }

    @Override
    public Recommendation generateRecommendation( int index, boolean active,
            String title, String recommendationText,
            Date creationDate, Date expirationDate )
            throws RecommendationServiceException {

        logger.debug( "creating recommendation" );
        Recommendation recommendation = new Recommendation();
        recommendation.setActive( active );
        recommendation.setIndex( index );
        recommendation.setTitle( title );
        recommendation.setType( Recommendation.USER_TYPE );
        recommendation.setContent( recommendationText );
        recommendation.setGenerationDate( creationDate );
        recommendation.setExpirationDate( expirationDate );
        logger.debug( "recommendation created" );
        return recommendation;
    }

    @Override
    public String generateUserRecommendation( int index, boolean active,
            String title, String userId,
            String recommendationText, Date creationDate, Date expirationDate )
            throws RecommendationServiceException {
        logger.debug( "Creating recommendation for user" );
        Recommendation recommendation = null;
        recommendation = this.generateRecommendation( index, active, title,
                recommendationText, creationDate, expirationDate );
        Set<String> userIds = new HashSet<String>();

        userIds.add( userId );
        recommendation.setUserIds( userIds );

        try {
            logger.debug( "recommendation 3" );
            this.dao.save( recommendation );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        return recommendation.getResourceId();
    }

    @Override
    public List<String> getAllAnnouncementIds()
            throws RecommendationServiceException {
        List<String> announcements = new ArrayList<String>();
        RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
        criteria.setRecommendationType( "Announcement" );
        List<Recommendation> result;
        try {
            result = this.dao.search( criteria );
            for ( Recommendation announcement : result ) {
                announcements.add( announcement.getResourceId() );
            }
            return announcements;
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
    }

    @Override
    public List<Recommendation> getAllAnnouncements()
            throws RecommendationServiceException {
        try {
            RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
            criteria.setRecommendationType( Recommendation.ANOUCHMENT_TYPE );

            List<Recommendation> result = dao.search( criteria );
            List<Recommendation> sortedList = new ArrayList<Recommendation>();

            Date today = new Date();
            for( Recommendation rec :result ){
                if( rec.getExpirationDate().after( today ) && rec.isActive() ) {
                    sortedList.add( rec );
                }
            }

            Collections.sort( sortedList, new RecommendationComparator() );

            if ( logger.isDebugEnabled() ) {
                logger.debug( "announcements size: " + result.size() );
                for ( Recommendation r : sortedList ) {
                    logger.debug( r.getContent() );
                }
            }

            return sortedList;
        } catch ( DAOException de ) {
            logger.error( de );
            throw new RecommendationServiceException(
                    "Error getting all announcements", de );
        }
    }

    @Override
    public List<String> getAllCommunityRecommendations()
            throws RecommendationServiceException {
        List<String> communityRecommendations = new ArrayList<String>();
        RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
        criteria.setRecommendationType( "Community" );
        List<Recommendation> result;
        try {
            result = this.dao.search( criteria );
            for ( Recommendation communityRecommendation : result ) {
                communityRecommendations.add( communityRecommendation.getContent() );
            }
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        return communityRecommendations;
    }

    @Override
    public List<Recommendation> getAllCommunityRecommendationsObj()
            throws RecommendationServiceException {
        List<Recommendation> communityRecommendations = new ArrayList<Recommendation>();
        RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
        criteria.setRecommendationType( "Community" );
        List<Recommendation> result;
        try {
            result = this.dao.search( criteria );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        for ( Recommendation communityRecommendation : result ) {
            if ( communityRecommendation == null ) {
                logger.debug( "Recommendation is null" );
            }
            communityRecommendations.add( communityRecommendation );
        }
        return communityRecommendations;
    }

    @Override
    public List<String> getAllCommunityRecommendations( String communityId )
            throws RecommendationServiceException {
        List<String> communityRecommendations = new ArrayList<String>();

        RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
        criteria.setRecommendationType( "Community" );
        criteria.setCommunityId( communityId );
        List<Recommendation> result;
        try {
            result = this.dao.search( criteria );
            for ( Recommendation communityRecommendation : result ) {
                communityRecommendations.add( communityRecommendation.getContent() );
            }
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        return communityRecommendations;
    }

    public List<Recommendation> getAllCommunityRecommendationsObj( String communityId )
            throws RecommendationServiceException {
        List<Recommendation> communityRecommendations = new ArrayList<Recommendation>();
        RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
        criteria.setRecommendationType( "Community" );
        criteria.setCommunityId( communityId );
        try {
            communityRecommendations = this.dao.search( criteria );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        return communityRecommendations;
    }

    @Override
    public List<String> getAllUserRecommendations( String userId )
            throws RecommendationServiceException {
        List<String> userRecommendations = new ArrayList<String>();
        RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
        criteria.setRecommendationType( "User" );
        criteria.setUserId( userId );
        List<Recommendation> result;
        try {
            result = this.dao.search( criteria );
            for ( Recommendation userRecommendation : result ) {
                userRecommendations.add( userRecommendation.getContent() );
            }
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }

        return userRecommendations;
    }

    @Override
    public List<String> getAnnouncements()
            throws RecommendationServiceException {
        try {
            Date today = new Date();
            List<String> announcements = new ArrayList<String>();
            RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();

            criteria.setExpiredAfter( today );
            criteria.setRecommendationType( Recommendation.ANOUCHMENT_TYPE );

            List<Recommendation> rs = this.dao.search( criteria );

            for ( Recommendation announcement : rs ) {
                announcements.add( announcement.getContent() );
            }

            return announcements;
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
    }

    @Override
    public List<String> getCommunityRecommendations( String communityId )
            throws RecommendationServiceException {
        List<String> communityRecommendations = new ArrayList<String>();
        RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
        criteria.setRecommendationType( "Community" );
        criteria.setCommunityId( communityId );
        criteria.setExpiredAfter( new Date() );
        List<Recommendation> result;
        try {
            result = this.dao.search( criteria );
            for ( Recommendation communityRecommendation : result ) {
                communityRecommendations.add( communityRecommendation.getContent() );
            }
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        return communityRecommendations;
    }

    @Override
    public List<Recommendation> getCommunityRecommendationsObj( String communityId )
            throws RecommendationServiceException {
        List<Recommendation> communityRecommendations = new ArrayList<Recommendation>();
        RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
        criteria.setRecommendationType( "Community" );
        criteria.setCommunityId( communityId );
        criteria.setExpiredAfter( new Date() );
        try {
            communityRecommendations = this.dao.search( criteria );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        return communityRecommendations;
    }

    @Override
    public List<String> getCommunityRecommendationsForUser( String userId )
            throws RecommendationServiceException {
        List<String> communityRecommendations = new ArrayList<String>();

        RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
        criteria.setRecommendationType( Recommendation.USER_TYPE );
        criteria.setUserId( userId );
        List<Recommendation> result;
        try {
            result = this.dao.search( criteria );

            for ( Recommendation rec : result ) {
                communityRecommendations.add( rec.getContent() );
            }
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        logger.debug( "I have been asked for user " + userId + " and i have " + communityRecommendations.size() + " for him" );
        return communityRecommendations;
    }

    @Override
    public List<Recommendation> getCommunityRecommendationsForUserObj( String userId )
            throws RecommendationServiceException {
        List<Recommendation> communityRecommendations = new ArrayList<Recommendation>();

        RecommendationSearchCriteria criteria = new RecommendationSearchCriteria();
        criteria.setRecommendationType( Recommendation.USER_TYPE );
        criteria.setUserId( userId );
        try {
            communityRecommendations = this.dao.search( criteria );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        logger.debug( "I have been asked for user " + userId + " and i have " + communityRecommendations.size() + " for him" );
        return communityRecommendations;
    }

    @Override
    public Recommendation getRecommendation( String recommendationId )
            throws RecommendationServiceException {
        try {
            return this.dao.getById( recommendationId );
        } catch ( DAOException e ) {
            logger.debug( e );
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
    }

    @Override
    public String getRecommendationText( String recommendationId )
            throws RecommendationServiceException {
        try {
            return this.dao.getById( recommendationId ).getContent();
        } catch ( DAOException e ) {
            logger.debug( e );
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
    }

    @Override
    public List<String> getRecommendations( List<String> recommendationIds )
            throws RecommendationServiceException {
        List<String> recommendations = new ArrayList<String>();
        for ( String recommendationId : recommendationIds ) {
            try {
                recommendations.add( this.dao.getById( recommendationId ).getContent() );
            } catch ( DAOException e ) {
                e.printStackTrace();
                throw new RecommendationServiceException( e );
            }

        }
        return recommendations;
    }

    @Override
    public void removeAnnouncement( String announcementId )
            throws RecommendationServiceException {
        this.removeRecommendation( announcementId );
    }

    @Override
    public void removeCommunityRecommendation( String recommendationId )
            throws RecommendationServiceException {
        this.removeRecommendation( recommendationId );
    }

    @Override
    public void removeRecommendation( String recommendationId )
            throws RecommendationServiceException {
        logger.debug( "deleting recommendation " + recommendationId );
        Recommendation recommendation = null;
        try {
            recommendation = this.dao.getById( recommendationId );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException(
                    "The recommendation cannot be found", e );
        }
        if ( recommendation != null ) {
            try {
                logger.debug( "Removing recommendation with id " + recommendation.getResourceId() );
                this.dao.delete( recommendation );
            } catch ( DAOException e ) {
                e.printStackTrace();
                throw new RecommendationServiceException(
                        "The recommendation cannot be deleted", e );
            }

        } else {
            throw new RecommendationServiceException(
                    "The recommendation does not exist" );
        }

    }

    @Override
    public void removeUserRecommendation( String recommendationId )
            throws RecommendationServiceException {
        this.removeRecommendation( recommendationId );
    }

    @Override
    public void updateAnnouncement( String announcementId, int index,
            boolean active, String announcementTitle, String announcementText,
            Date creationDate, Date expirationDate )
            throws RecommendationServiceException {
        this.updateRecommendation( announcementId, index, active, announcementTitle,
                announcementText, creationDate, expirationDate );
    }

    @Override
    public void updateCommunityRecommendation( String recommendationId, int index,
            boolean active, String title,
            String recommendationText, Date creationDate, Date expirationDate,
            Set<String> communityIds ) throws RecommendationServiceException {
        Recommendation recommendation = null;

        try {
            recommendation = this.dao.getById( recommendationId );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        recommendation.setCommunityIds( communityIds );
        try {
            this.dao.save( recommendation );
        } catch ( DAOException e ) {
            e.printStackTrace();
            throw new RecommendationServiceException( e );
        }
        this.updateRecommendation( recommendationId, index, active, title,
                recommendationText, creationDate, expirationDate );
    }

    @Override
    public void updateRecommendation( String recommendationId, int index,
            boolean active, String title,
            String recommendationText, Date creationDate, Date expirationDate )
            throws RecommendationServiceException {
        Recommendation recommendation = null;
        try {
            recommendation = this.dao.getById( recommendationId );
        } catch ( DAOException e1 ) {
            e1.printStackTrace();
            throw new RecommendationServiceException(
                    "The recommendation cannot be found", e1 );
        }
        if ( recommendation != null ) {
            recommendation.setExpirationDate( expirationDate );
            recommendation.setGenerationDate( creationDate );
            recommendation.setContent( recommendationText );
            recommendation.setActive( active );
            recommendation.setIndex( index );
            recommendation.setTitle( title );

            try {
                this.dao.save( recommendation );
            } catch ( DAOException e ) {
                e.printStackTrace();
                throw new RecommendationServiceException(
                        "The recommendation cannot be updated" );
            }
        } else {
            throw new RecommendationServiceException(
                    "The recommendation does not exist" );
        }
    }

    public void setQueryRunning( boolean val ) {
//        this.queryRunning = val;
    }

    public void sendMail( String userEmailAddress, String announcementText,
            String subject ) throws MessagingException {

        if ( this.sendMail == false ) {
            logger.debug( "I will not send an email becouse i am configured not to do it" );
            return;
        }
        // Set the host smtp address
        // Properties props = new Properties();
        // props.put( "mail.smtp.host", preferences.getMailSmtpHost() );
        // create some properties and get the default Session
        this.preferences.setPref( "mail.smtp.host", this.mailHost );
        this.preferences.setPref( "mail.smtp.port", this.mailPort );

        Session session = Session.getDefaultInstance(
                this.preferences.getPref(), null );

        // Session session = Session.getDefaultInstance( props, null );
        // create a message
        MimeMessage mailMessage = new MimeMessage( session );
        mailMessage.setFrom( new InternetAddress( "non-reply@di.uoa.gr" ) );
        mailMessage.addRecipient( javax.mail.Message.RecipientType.TO,
                new InternetAddress( userEmailAddress ) );
        mailMessage.setSubject( subject );
        mailMessage.setText( announcementText );
        Transport.send( mailMessage );

    }

    public QueryHashDAO getQueryHashDao() {
        return queryHashDao;
    }

    public void setQueryHashDao( QueryHashDAO queryHashDao ) {
        this.queryHashDao = queryHashDao;
    }

    public RecommendationDAO getDao() {
        return dao;
    }

    public String getWebServiceUrl() {
        return this.getServiceEPR().getAddress();
    }

    public ArrayList<UserProfile> getUserOfCommunity( String communityId ) throws UserProfileServiceException {
        UserProfileService service = profileServiceLocator.getService();
        // criteria.setBelongsToCommunity( communityId );
        UserProfileSearchCriteria criteria = new UserProfileSearchCriteria();
        criteria.setBelongsToCommunity( communityId );
        return (ArrayList<UserProfile>) service.searchUsers( criteria );				//
    }

    public String generateCommunityRecommendationForUsers( int index, boolean active,
            String title, String recommendationText,
            Date creationDate, Date expirationDate, Set<String> communityIds,
            Set<String> userIds ) throws RecommendationServiceException, DAOException {
        logger.debug( "creating community recommendation for users" );
        for ( String id : userIds ) {
            logger.debug( id );
        }
        Recommendation communityRecommendation = null;
        communityRecommendation = this.generateRecommendation( index,
                active,
                title,
                recommendationText, expirationDate, expirationDate );
        communityRecommendation.setType( Recommendation.COMMUNITY_TYPE );
        communityRecommendation.setCommunityIds( communityIds );
        communityRecommendation.setUserIds( userIds );

        communityRecommendation = this.dao.save( communityRecommendation );

        return communityRecommendation.getResourceId();
    }

    @Override
    public void swapAnnouncements( String announcementId1, String announcementId2 )
            throws RecommendationServiceException {
        try {
            Recommendation announcement1 = this.dao.getById( announcementId1 );
            Recommendation announcement2 = this.dao.getById( announcementId2 );
            int tmp = announcement1.getIndex();
            announcement1.setIndex( announcement2.getIndex() );
            announcement2.setIndex( tmp );
            this.dao.save( announcement1 );
            this.dao.save( announcement2 );
        } catch ( DAOException e ) {
            throw new RecommendationServiceException( e );
        }

    }

    public void swapCommunityRecommendations( String recommendationId1, String recommendationId2 ) throws RecommendationServiceException {
        swapAnnouncements( recommendationId1, recommendationId2 );
    }

    public void swapUserRecommendations( String recommendationId1, String recommendationId2 ) throws RecommendationServiceException {
        swapAnnouncements( recommendationId1, recommendationId2 );
    }

    /**
     * @return the userProfilesLookup
     */
    public ISLookUp<UserProfile> getUserProfilesLookup() {
        return userProfilesLookup;
    }

    /**
     * @param userProfilesLookup the userProfilesLookup to set
     */
    public void setUserProfilesLookup( ISLookUp<UserProfile> userProfilesLookup ) {
        this.userProfilesLookup = userProfilesLookup;
    }

    public void setRsFactory( ResultSetFactory rsFactory ) {
        this.rsFactory = rsFactory;
    }

    /**
     * @return the queryHashLookup
     */
    public ISLookUp<QueryHash> getQueryHashLookup() {
        return queryHashLookup;
    }

    /**
     * @param queryHashLookup the queryHashLookup to set
     */
    public void setQueryHashLookup( ISLookUp<QueryHash> queryHashLookup ) {
        this.queryHashLookup = queryHashLookup;
    }

    /**
     * @return the maxDocumentsForHashing
     */
    public int getMaxDocumentsForHashing() {
        return maxDocumentsForHashing;
    }

    /**
     * @param maxDocumentsForHashing the maxDocumentsForHashing to set
     */
    public void setMaxDocumentsForHashing( int maxDocumentsForHashing ) {
        this.maxDocumentsForHashing = maxDocumentsForHashing;
    }

    /**
     * @return the mailPeriod
     */
    public int getMailPeriod() {
        return mailPeriod;
    }

    /**
     * @param mailPeriod the mailPeriod to set
     */
    public void setMailPeriod( int mailPeriod ) {
        this.mailPeriod = mailPeriod;
    }

    /**
     * @return the savedQueryMailSub
     */
    public String getSavedQueryMailSub() {
        return savedQueryMailSub;
    }

    /**
     * @param savedQueryMailSub the savedQueryMailSub to set
     */
    public void setSavedQueryMailSub( String savedQueryMailSub ) {
        this.savedQueryMailSub = savedQueryMailSub;
    }

    /**
     * @return the savedQueryMailMsg
     */
    public String getSavedQueryMailMsg() {
        return savedQueryMailMsg;
    }

    /**
     * @param savedQueryMailMsg the savedQueryMailMsg to set
     */
    public void setSavedQueryMailMsg( String savedQueryMailMsg ) {
        this.savedQueryMailMsg = savedQueryMailMsg;
    }

	public void setCommunityNotificationManager(
			CommunityNotificationManager communityNotificationManager) {
		this.communityNotificationManager = communityNotificationManager;
	}
}
class QueryRunner implements Runnable {

    RecommendationServiceImpl parrent;

    QueryRunner( RecommendationServiceImpl parrent ) {
        this.parrent = parrent;
    }

    public void run() {
        this.parrent.initSearchQueryCheckOut();
    }

    public void start() {
        new Thread( this ).start();
    }
}
